Пример #1
0
static void m2_swipe_nearby_items(short player_index)
{
	object_data *object;
	object_data *player_object;
	player_data *player= get_player_data(player_index);
	short next_object;
	polygon_data *polygon;
	short *neighbor_indexes;
	short i;

	player_object= get_object_data(get_monster_data(player->monster_index)->object_index);

	polygon= get_polygon_data(player_object->polygon);
	neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
	
	// Skip this step if neighbor indexes were not found
	if (!neighbor_indexes) return;

	for (i=0;i<polygon->neighbor_count;++i)
	{	
		
		polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
	
		if (POLYGON_IS_DETACHED(neighboring_polygon))
			continue;
	
		next_object= neighboring_polygon->first_object;

		while(next_object != NONE)
		{
			object= get_object_data(next_object);
			if (GET_OBJECT_OWNER(object)==_object_is_item && !OBJECT_IS_INVISIBLE(object)) 
			{
				if (guess_distance2d((world_point2d *) &player->location, (world_point2d *) &object->location)<=MAXIMUM_ARM_REACH)
				{
					world_distance radius, height;
					
					get_monster_dimensions(player->monster_index, &radius, &height);
	
					if (object->location.z >= player->location.z - MAXIMUM_ARM_REACH && object->location.z <= player->location.z + height &&
						test_item_retrieval(player_object->polygon, &player_object->location, &object->location))
					{
						if(get_item(player_index, next_object))
						{
							/* Start the search again.. */
							next_object= neighboring_polygon->first_object;
							continue;
						}
					}
				}
			}
			
			next_object= object->next_object;
		}
	
	}
}
Пример #2
0
void try_and_toggle_control_panel(
	short polygon_index,
	short line_index)
{
	struct polygon_data *polygon= get_polygon_data(polygon_index);
	struct line_data *line= get_line_data(line_index);
	short side_index= find_adjacent_side(polygon_index, line_index);
	
	if (side_index!=NONE)
	{
		struct side_data *side= get_side_data(side_index);

		if (SIDE_IS_CONTROL_PANEL(side))
		{
			if (switch_can_be_toggled(side_index, FALSE))
			{
				boolean make_sound, state= GET_CONTROL_PANEL_STATUS(side);
				struct control_panel_definition *definition= get_control_panel_definition(side->control_panel_type);
				
				switch (definition->panel_class)
				{
					case _panel_is_tag_switch:
						state= !state;
						make_sound= set_tagged_light_statuses(side->control_panel_permutation, state);
						if (try_and_change_tagged_platform_states(side->control_panel_permutation, state)) make_sound= TRUE;
						if (!side->control_panel_permutation) make_sound= TRUE;
						if (make_sound)
						{
							SET_CONTROL_PANEL_STATUS(side, state);
							set_control_panel_texture(side);
						}
						break;
					case _panel_is_light_switch:
						state= !state;
						make_sound= set_light_status(side->control_panel_permutation, state);
						break;
					case _panel_is_platform_switch:
						state= !state;
						make_sound= try_and_change_platform_state(get_polygon_data(side->control_panel_permutation)->permutation, state);
						break;
				}
				
				if (make_sound)
				{
					play_control_panel_sound(side_index, state ? _activating_sound : _deactivating_sound);
				}
			}
		}
	}
	
	return;
}
Пример #3
0
boolean untoggled_repair_switches_on_level(
	void)
{
	short side_index;
	struct side_data *side;
	boolean untoggled_switch= FALSE;
	
	for (side_index= 0, side= map_sides; side_index<dynamic_world->side_count && !untoggled_switch; ++side_index, ++side)
	{
		if (SIDE_IS_CONTROL_PANEL(side) && SIDE_IS_REPAIR_SWITCH(side))
		{
			struct control_panel_definition *definition= get_control_panel_definition(side->control_panel_type);
			
			switch (definition->panel_class)
			{
				case _panel_is_platform_switch:
					untoggled_switch= platform_is_at_initial_state(get_polygon_data(side->control_panel_permutation)->permutation) ? TRUE : FALSE;
					break;
				
				default:
					untoggled_switch= GET_CONTROL_PANEL_STATUS(side) ? FALSE : TRUE;
					break;
			}
		}
	}
	
	return untoggled_switch;
}
Пример #4
0
void adjust_player_for_polygon_height_change(
	short monster_index,
	short polygon_index,
	world_distance new_floor_height,
	world_distance new_ceiling_height)
{
	short player_index= monster_index_to_player_index(monster_index);
	struct player_data *player= get_player_data(player_index);
	struct physics_variables *variables= &player->variables;
	struct polygon_data *polygon= get_polygon_data(polygon_index);
	world_distance old_floor_height= polygon->floor_height;

	(void) (new_ceiling_height);

	if (player->supporting_polygon_index==polygon_index)
	{
		if (FIXED_TO_WORLD(variables->position.z)<=old_floor_height) /* must be <= */
		{
			variables->floor_height= variables->position.z= WORLD_TO_FIXED(new_floor_height);
			if (film_profile.fix_sliding_on_platforms && variables->external_velocity.k < 0) 
			{
				variables->external_velocity.k = 0;
			}

			if (PLAYER_IS_DEAD(player)) variables->external_velocity.k= 0;
		}
	}
}
Пример #5
0
int Lua_Monster_Move_By_Path(lua_State *L)
{
    int monster_index = Lua_Monster::Index(L, 1);
    int polygon_index = 0;
    if (lua_isnumber(L, 2))
    {
        polygon_index = static_cast<int>(lua_tonumber(L, 2));
        if (!Lua_Polygon::Valid(polygon_index))
            return luaL_error(L, "move_by_path: invalid polygon index");
    }
    else if (Lua_Polygon::Is(L, 2))
    {
        polygon_index = Lua_Polygon::Index(L, 2);
    }
    else
        return luaL_error(L, "move_by_path: incorrect argument type");

    monster_data *monster = get_monster_data(monster_index);
    if (MONSTER_IS_PLAYER(monster))
        return luaL_error(L, "move_by_path: monster is player");

    monster_definition *definition = get_monster_definition_external(monster->type);
    object_data *object = get_object_data(monster->object_index);
    monster_pathfinding_data path;
    world_point2d destination;

    if (!MONSTER_IS_ACTIVE(monster))
        activate_monster(monster_index);

    if (monster->path != NONE)
    {
        delete_path(monster->path);
        monster->path = NONE;
    }

    SET_MONSTER_NEEDS_PATH_STATUS(monster, false);
    path.definition = definition;
    path.monster = monster;
    path.cross_zone_boundaries = true;

    destination = get_polygon_data(polygon_index)->center;

    monster->path = new_path((world_point2d *) &object->location, object->polygon, &destination, polygon_index, 3 * definition->radius, monster_pathfinding_cost_function, &path);
    if (monster->path == NONE)
    {
        if (monster->action != _monster_is_being_hit || MONSTER_IS_DYING(monster))
        {
            set_monster_action(monster_index, _monster_is_stationary);
        }
        set_monster_mode(monster_index, _monster_unlocked, NONE);
        return 0;
    }

    advance_monster_path(monster_index);
    return 0;
}
Пример #6
0
void cause_polygon_damage(
	short polygon_index,
	short monster_index)
{
	struct polygon_data *polygon= get_polygon_data(polygon_index);
	struct monster_data *monster= get_monster_data(monster_index);
	struct object_data *object= get_object_data(monster->object_index);
    
    short polygon_type = polygon->type;
    // apply damage from flooded platforms
    if (polygon->type == _polygon_is_platform)
	{
		struct platform_data *platform= get_platform_data(polygon->permutation);
		if (platform && PLATFORM_IS_FLOODED(platform))
		{
			short adj_index = find_flooding_polygon(polygon_index);
			if (adj_index != NONE)
			{
				struct polygon_data *adj_polygon = get_polygon_data(adj_index);
				polygon_type = adj_polygon->type;
			}
		}
	}


// #if 0
	if ((polygon_type==_polygon_is_minor_ouch && !(dynamic_world->tick_count&MINOR_OUCH_FREQUENCY) && object->location.z==polygon->floor_height) ||
		(polygon_type==_polygon_is_major_ouch && !(dynamic_world->tick_count&MAJOR_OUCH_FREQUENCY)))
	{
		struct damage_definition damage;
		
		damage.flags= _alien_damage;
		damage.type= polygon_type==_polygon_is_minor_ouch ? _damage_polygon : _damage_major_polygon;
		damage.base= polygon_type==_polygon_is_minor_ouch ? MINOR_OUCH_DAMAGE : MAJOR_OUCH_DAMAGE;
		damage.random= 0;
		damage.scale= FIXED_ONE;
		
		damage_monster(monster_index, NONE, NONE, (world_point3d *) NULL, &damage, NONE);
	}
// #endif
}
Пример #7
0
static int32 item_trigger_cost_function(short source_polygon_index, short line_index, short destination_polygon_index, void *unused)
{
	polygon_data *destination_polygon = get_polygon_data(destination_polygon_index);

	auto cost = 1;
	
	(void) (unused);
	(void) (source_polygon_index);
	(void) (line_index);

	if (destination_polygon->type==_polygon_is_zone_border) 
		cost = -1;
	
	return cost;
}
Пример #8
0
/* false means donÕt fire this (itÕs in a floor or ceiling or outside of the map), otherwise
	the monster that was intersected first (or NONE) is returned in target_index */
bool preflight_projectile(
	world_point3d *origin,
	short origin_polygon_index,
	world_point3d *destination,
	angle delta_theta,
	short type,
	short owner,
	short owner_type,
	short *obstruction_index)
{
	bool legal_projectile= false;
	struct projectile_definition *definition= get_projectile_definition(type);
	
	(void) (delta_theta);
	
	/* will be used when we truly preflight projectiles */
	(void) (owner_type);
	
	if (origin_polygon_index!=NONE)
	{
		world_distance dx= destination->x-origin->x, dy= destination->y-origin->y;
		angle elevation= arctangent(isqrt(dx*dx + dy*dy), destination->z-origin->z);
		
		if (elevation<MAXIMUM_PROJECTILE_ELEVATION || elevation>FULL_CIRCLE-MAXIMUM_PROJECTILE_ELEVATION)
		{
			struct polygon_data *origin_polygon= get_polygon_data(origin_polygon_index);
			
			// LP note: "penetrates media boundary" means "hit media surface and continue";
			// it will act like "penetrates_media" here
			// Added idiot-proofing to media data
			media_data *media = get_media_data(origin_polygon->media_index);
			if (origin->z>origin_polygon->floor_height && origin->z<origin_polygon->ceiling_height &&
				(origin_polygon->media_index==NONE || definition->flags&(_penetrates_media) || (media ? origin->z>media->height : true)))
			{
				/* make sure it hits something */
				uint16 flags= translate_projectile(type, origin, origin_polygon_index, destination, (short *) NULL, owner, obstruction_index, 0, true, NONE);
				
				*obstruction_index= (flags&_projectile_hit_monster) ? get_object_data(*obstruction_index)->permutation : NONE;
				legal_projectile= true;
			}
		}
	}
	
	return legal_projectile;
}
Пример #9
0
void trigger_nearby_items(short polygon_index)
{
	polygon_index = flood_map(polygon_index, INT32_MAX, item_trigger_cost_function, _breadth_first, nullptr);
	while(polygon_index != NONE)
	{
		object_data *object;

		for(auto object_index = get_polygon_data(polygon_index)->first_object; object_index != NONE; object_index = object->next_object)
		{
			object = get_object_data(object_index);
			switch (GET_OBJECT_OWNER(object))
			{
				case _object_is_item:
					if (OBJECT_IS_INVISIBLE(object) && object->permutation!=NONE)
					{
						teleport_object_in(object_index);
					}
					break;
			}
		}
		
		polygon_index= flood_map(NONE, INT32_MAX, item_trigger_cost_function, _breadth_first, nullptr);
	}
}
Пример #10
0
/* set the initial states of all switches based on the objects they control */
void initialize_control_panels_for_level(
	void)
{
	short side_index;
	struct side_data *side;

	for (side_index= 0, side= map_sides; side_index<dynamic_world->side_count; ++side, ++side_index)
	{
		if (SIDE_IS_CONTROL_PANEL(side))
		{
			struct control_panel_definition *definition= get_control_panel_definition(side->control_panel_type);
			boolean status= FALSE;
			
			switch (definition->panel_class)
			{
				case _panel_is_tag_switch:
					status= GET_CONTROL_PANEL_STATUS(side);
					// use default position
					break;
				
				case _panel_is_light_switch:
					status= get_light_status(side->control_panel_permutation);
					break;
				
				case _panel_is_platform_switch:
					if (platform_is_on(get_polygon_data(side->control_panel_permutation)->permutation)) status= TRUE;
					break;
			}
			
			SET_CONTROL_PANEL_STATUS(side, status);
			set_control_panel_texture(side);
		}
	}

	return;
}
Пример #11
0
static bool test_item_retrieval(short polygon_index1, world_point3d *location1, world_point3d *location2)
{
	bool valid_retrieval= true;
	auto polygon_index = polygon_index1;

	do
	{
		auto line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *) location1,
			(world_point2d *) location2);
		
		if (line_index!=NONE)
		{
			polygon_index= find_adjacent_polygon(polygon_index, line_index);
			if (LINE_IS_SOLID(get_line_data(line_index))) 
				valid_retrieval= false;
			if (polygon_index!=NONE)
			{
				polygon_data *polygon= get_polygon_data(polygon_index);
				
				if (polygon->type==_polygon_is_platform)
				{
					platform_data *platform= get_platform_data(polygon->permutation);
					
					if (PLATFORM_IS_MOVING(platform)) valid_retrieval= false;
				}
			}
		}
		else
		{
			polygon_index= NONE;
		}
	}
	while (polygon_index!=NONE && valid_retrieval);
	
	return valid_retrieval;
}
Пример #12
0
/* else it is that we can think of. */
void changed_polygon(
	short original_polygon_index,
	short new_polygon_index,
	short player_index)
{
	struct polygon_data *new_polygon= get_polygon_data(new_polygon_index);
	struct player_data *player= player_index!=NONE ? get_player_data(player_index) : (struct player_data *) NULL;
	
	(void) (original_polygon_index);
	
	/* Entering this polygon.. */
	switch (new_polygon->type)
	{
		case _polygon_is_visible_monster_trigger:
			if (player)
			{
				activate_nearby_monsters(player->monster_index, player->monster_index,
					_pass_solid_lines|_activate_deaf_monsters|_use_activation_biases|_activation_cannot_be_avoided);
			}
			break;
		case _polygon_is_invisible_monster_trigger:
		case _polygon_is_dual_monster_trigger:
			if (player)
			{
				activate_nearby_monsters(player->monster_index, player->monster_index,
					_pass_solid_lines|_activate_deaf_monsters|_activate_invisible_monsters|_use_activation_biases|_activation_cannot_be_avoided);
			}
			break;
		
		case _polygon_is_item_trigger:
			if (player)
			{
				trigger_nearby_items(new_polygon_index);
			}
			break;

		case _polygon_is_light_on_trigger:
		case _polygon_is_light_off_trigger:
			set_light_status(new_polygon->permutation,
				new_polygon->type==_polygon_is_light_off_trigger ? false : true);
			break;
			
		case _polygon_is_platform:
			platform_was_entered(new_polygon->permutation, player ? true : false);
			break;
		case _polygon_is_platform_on_trigger:
		case _polygon_is_platform_off_trigger:
			if (player)
			{
				try_and_change_platform_state(get_polygon_data(new_polygon->permutation)->permutation,
					new_polygon->type==_polygon_is_platform_off_trigger ? false : true);
			}
			break;
			
		case _polygon_must_be_explored:
			/* When a player enters a must be explored, it now becomes a normal polygon, to allow */
			/*  for must be explored flags to work across cooperative net games */
			if(player)
			{
				new_polygon->type= _polygon_is_normal;
			}
			break;
			
		default:
			break;
	}
}
Пример #13
0
static short find_action_key_target(
	short player_index,
	world_distance range,
	short *target_type)
{
	struct player_data *player= get_player_data(player_index);
	short current_polygon= player->camera_polygon_index;
	world_point2d destination;
	boolean done= FALSE;
	short itemhit, line_index;
	struct polygon_data *polygon;

	/* Should we use this one, the physics one, or the object one? */
	ray_to_line_segment((world_point2d *) &player->location, &destination, player->facing, range);

//	dprintf("#%d(#%d,#%d) --> (#%d,#%d) (#%d along #%d)", current_polygon, player->location.x, player->location.y, destination.x, destination.y, range, player->facing);

	itemhit= NONE;
	while (!done)
	{
		line_index= find_line_crossed_leaving_polygon(current_polygon, (world_point2d *) &player->location, &destination);
			
		if (line_index==NONE)
		{
			done= TRUE;
		} 
		else 
		{
			struct line_data *line;
			short original_polygon;

			line= get_line_data(line_index);
			original_polygon= current_polygon;
			current_polygon= find_adjacent_polygon(current_polygon, line_index);
			
//			dprintf("leaving polygon #%d through line #%d to polygon #%d", original_polygon, line_index, current_polygon);
			
			if (current_polygon!=NONE)
			{
				polygon= get_polygon_data(current_polygon);

				/* We hit a platform */				
				if (polygon->type==_polygon_is_platform && line_is_within_range(player->monster_index, line_index, MAXIMUM_PLATFORM_ACTIVATION_RANGE) &&
					platform_is_legal_player_target(polygon->permutation))
				{
//					dprintf("found platform #%d in %p", polygon->permutation, polygon);
					itemhit= polygon->permutation;
					*target_type= _target_is_platform;
					done= TRUE;
				} 
			} 
			else 
			{
				done= TRUE;
			}

			/* Slammed a wall */
			if (line_is_within_range(player->monster_index, line_index, MAXIMUM_CONTROL_ACTIVATION_RANGE))
			{
				if (line_side_has_control_panel(line_index, original_polygon, &itemhit))
				{
					if (switch_can_be_toggled(itemhit, TRUE))
					{
						*target_type= _target_is_control_panel;
						done= TRUE;
					}
					else
					{
						itemhit= NONE;
					}
				}
			}
		}
	}
	
	return itemhit;
}
Пример #14
0
static void update_view_data(
	struct view_data *view)
{
	angle theta;

	// LP change: doing all the FOV changes here:
	View_AdjustFOV(view->field_of_view,view->target_field_of_view);
	
	if (view->effect==NONE)
	{
		view->world_to_screen_x= view->real_world_to_screen_x;
		view->world_to_screen_y= view->real_world_to_screen_y;
	}
	else
	{
		update_render_effect(view);
	}
	
	view->untransformed_left_edge.i= view->world_to_screen_x;
	view->untransformed_right_edge.i= - view->world_to_screen_x;
	
	/* calculate world_to_screen_y*tan(pitch) */
	view->dtanpitch= (view->world_to_screen_y*sine_table[view->pitch])/cosine_table[view->pitch];

	/* calculate left cone vector */
	theta= NORMALIZE_ANGLE(view->yaw-view->half_cone);
	view->left_edge.i= cosine_table[theta], view->left_edge.j= sine_table[theta];
	
	/* calculate right cone vector */
	theta= NORMALIZE_ANGLE(view->yaw+view->half_cone);
	view->right_edge.i= cosine_table[theta], view->right_edge.j= sine_table[theta];
	
	/* calculate top cone vector (negative to clip the right direction) */
	view->top_edge.i= - view->world_to_screen_y;
	view->top_edge.j= - (view->half_screen_height + view->dtanpitch); /* ==k */

	/* calculate bottom cone vector */
	view->bottom_edge.i= view->world_to_screen_y;
	view->bottom_edge.j= - view->half_screen_height + view->dtanpitch; /* ==k */

	/* if weÕre sitting on one of the endpoints in our origin polygon, move us back slightly (±1) into
		that polygon.  when we split rays weÕre assuming that weÕll never pass through a given
		vertex in different directions (because if we do the tree becomes a graph) but when
		we start on a vertex this can happen.  this is a destructive modification of the origin. */
	{
		short i;
		struct polygon_data *polygon= get_polygon_data(view->origin_polygon_index);
		
		for (i= 0;i<polygon->vertex_count;++i)
		{
			struct world_point2d *vertex= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex;
			
			if (vertex->x==view->origin.x && vertex->y==view->origin.y)
			{
				world_point2d *ccw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_LOW(i, polygon->vertex_count-1)])->vertex;
				world_point2d *cw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)])->vertex;
				world_vector2d inset_vector;
				
				inset_vector.i= (ccw_vertex->x-vertex->x) + (cw_vertex->x-vertex->x);
				inset_vector.j= (ccw_vertex->y-vertex->y) + (cw_vertex->y-vertex->y);
				view->origin.x+= SGN(inset_vector.i);
				view->origin.y+= SGN(inset_vector.j);
				
				break;
			}
		}
		
		/* determine whether we are under or over the media boundary of our polygon; we will see all
			other media boundaries from this orientation (above or below) or fail to draw them. */
		if (polygon->media_index==NONE)
		{
			view->under_media_boundary= false;
		}
		else
		{
			struct media_data *media= get_media_data(polygon->media_index);
			
			// LP change: idiot-proofing
			if (media)
			{
				view->under_media_boundary= UNDER_MEDIA(media, view->origin.z);
				view->under_media_index= polygon->media_index;
			} else {
				view->under_media_boundary= false;
			}
		}
	}
}
Пример #15
0
/* assumes ¶t==1 tick */
void move_projectiles(
	void)
{
	struct projectile_data *projectile;
	short projectile_index;
	
	for (projectile_index=0,projectile=projectiles;projectile_index<MAXIMUM_PROJECTILES_PER_MAP;++projectile_index,++projectile)
	{
		if (SLOT_IS_USED(projectile))
		{
			struct object_data *object= get_object_data(projectile->object_index);
			
//			if (!OBJECT_IS_INVISIBLE(object))
			{
				struct projectile_definition *definition= get_projectile_definition(projectile->type);
				short old_polygon_index= object->polygon;
				world_point3d new_location, old_location;
				short obstruction_index, new_polygon_index;
				
				new_location= old_location= object->location;
	
				/* update our objectÕs animation */
				animate_object(projectile->object_index);
				
				/* if weÕre supposed to end when our animation loops, check this condition */
				if ((definition->flags&_stop_when_animation_loops) && (GET_OBJECT_ANIMATION_FLAGS(object)&_obj_last_frame_animated))
				{
					remove_projectile(projectile_index);
				}
				else
				{
					world_distance speed= definition->speed;
					uint32 adjusted_definition_flags = 0;
					uint16 flags;
					
					/* base alien projectile speed on difficulty level */
					if (definition->flags&_alien_projectile)
					{
						switch (dynamic_world->game_information.difficulty_level)
						{
							case _wuss_level: speed-= speed>>3; break;
							case _easy_level: speed-= speed>>4; break;
							case _major_damage_level: speed+= speed>>3; break;
							case _total_carnage_level: speed+= speed>>2; break;
						}
					}
	
					/* if this is a guided projectile with a valid target, update guidance system */				
					if ((definition->flags&_guided) && projectile->target_index!=NONE && (dynamic_world->tick_count&1)) update_guided_projectile(projectile_index);

					if (PROJECTILE_HAS_CROSSED_MEDIA_BOUNDARY(projectile)) adjusted_definition_flags= _penetrates_media;
					
					/* move the projectile and check for collisions; if we didnÕt detonate move the
						projectile and check to see if we need to leave a contrail */
					if ((definition->flags&_affected_by_half_gravity) && (dynamic_world->tick_count&1)) projectile->gravity-= GRAVITATIONAL_ACCELERATION;
					if (definition->flags&_affected_by_gravity) projectile->gravity-= GRAVITATIONAL_ACCELERATION;
					if (definition->flags&_doubly_affected_by_gravity) projectile->gravity-= 2*GRAVITATIONAL_ACCELERATION;
					if (film_profile.m1_low_gravity_projectiles && static_world->environment_flags&_environment_low_gravity && static_world->environment_flags&_environment_m1_weapons)
					{
						projectile->gravity /= 2;
					}
					new_location.z+= projectile->gravity;
					translate_point3d(&new_location, speed, object->facing, projectile->elevation);
					if (definition->flags&_vertical_wander) new_location.z+= (global_random()&1) ? WANDER_MAGNITUDE : -WANDER_MAGNITUDE;
					if (definition->flags&_horizontal_wander) translate_point3d(&new_location, (global_random()&1) ? WANDER_MAGNITUDE : -WANDER_MAGNITUDE, NORMALIZE_ANGLE(object->facing+QUARTER_CIRCLE), 0);
					if (film_profile.infinity_smg)
					{
						definition->flags ^= adjusted_definition_flags;
					}
					flags= translate_projectile(projectile->type, &old_location, object->polygon, &new_location, &new_polygon_index, projectile->owner_index, &obstruction_index, 0, false, projectile_index);
					if (film_profile.infinity_smg)
					{
						definition->flags ^= adjusted_definition_flags;
					}
					
					// LP change: set up for penetrating media boundary
					bool will_go_through = false;
					
					if (flags&_projectile_hit)
					{
						if ((flags&_projectile_hit_floor) && (definition->flags&_rebounds_from_floor) &&
							projectile->gravity<-MINIMUM_REBOUND_VELOCITY)
						{
							play_object_sound(projectile->object_index, definition->rebound_sound);
							projectile->gravity= - projectile->gravity + (projectile->gravity>>2); /* 0.75 */
						}
						else
						{
 							short monster_obstruction_index= (flags&_projectile_hit_monster) ? get_object_data(obstruction_index)->permutation : NONE;
							bool destroy_persistent_projectile= false;
							
							if (flags&_projectile_hit_scenery) damage_scenery(obstruction_index);
							
							/* cause damage, if we can */
							if (!PROJECTILE_HAS_CAUSED_DAMAGE(projectile))
							{
								struct damage_definition *damage= &definition->damage;
								
								damage->scale= projectile->damage_scale;
								if (definition->flags&_becomes_item_on_detonation)
								{
									if (monster_obstruction_index==NONE)
									{
										struct object_location location;
										
										location.p= object->location, location.p.z= 0;
										location.polygon_index= object->polygon;
										location.yaw= location.pitch= 0;
										location.flags= 0;
										// START Benad
										// Found it!
										// With new_item(), current_item_count[item] increases, but not
										// with try_and_add_player_item(). So reverse the effect of new_item in advance.
										dynamic_world->current_item_count[projectile->permutation]--;
										// END Benad
										new_item(&location, projectile->permutation);
										
										destroy_persistent_projectile= true;
									}
									else
									{
										if(MONSTER_IS_PLAYER(get_monster_data(monster_obstruction_index)))
										{
											short player_obstruction_index= monster_index_to_player_index(monster_obstruction_index);
											destroy_persistent_projectile= try_and_add_player_item(player_obstruction_index, projectile->permutation);
										}
									}
								}
								else
								{
									if (definition->area_of_effect)
									{
										damage_monsters_in_radius(monster_obstruction_index, projectile->owner_index, projectile->owner_type, &old_location, object->polygon, definition->area_of_effect, damage, projectile_index);
									}
									else
									{
										if (monster_obstruction_index!=NONE) damage_monster(monster_obstruction_index, projectile->owner_index, projectile->owner_type, &old_location, damage, projectile_index);
									}
								}
							}
              
							if ((definition->flags&_persistent) && !destroy_persistent_projectile)
							{
								SET_PROJECTILE_DAMAGE_STATUS(projectile, true);
							}
							else
							{
								short detonation_effect= definition->detonation_effect;
								
								if (monster_obstruction_index!=NONE)
								{
									if (definition->flags&_bleeding_projectile)
									{
										detonation_effect= get_monster_impact_effect(monster_obstruction_index);
									}
									if (definition->flags&_melee_projectile)
									{
										short new_detonation_effect= get_monster_melee_impact_effect(monster_obstruction_index);
										if (new_detonation_effect!=NONE) detonation_effect= new_detonation_effect;
									}
								}
								if (flags&_projectile_hit_media)
								{
									get_media_detonation_effect(get_polygon_data(obstruction_index)->media_index, definition->media_detonation_effect, &detonation_effect);
									// LP addition: check if projectile will hit media and continue (PMB flag)
									// set will_go_through for later processing
									if (film_profile.a1_smg)
									{
										// Be careful about parentheses here!
										will_go_through = (definition->flags&_penetrates_media_boundary) != 0;
										// Push the projectile upward or downward, if necessary
										if (will_go_through) {
											if (projectile->elevation == 0) {}
											else if (projectile->elevation < HALF_CIRCLE) new_location.z++;
											else if (projectile->elevation > HALF_CIRCLE) new_location.z--;
										}
									}
								}
								if (film_profile.a1_smg)
								{
									// LP addition: don't detonate if going through media
									// if PMB is set; otherwise, detonate if doing so.
									// Some of the later routines may set both "hit landscape" and "hit media",
									// so be careful.
									if (flags&_projectile_hit_landscape && !(flags&_projectile_hit_media)) detonation_effect= NONE;
								}
								else
								{
									if (flags&_projectile_hit_landscape) detonation_effect = NONE;
								}
								
								if (detonation_effect!=NONE) new_effect(&new_location, new_polygon_index, detonation_effect, object->facing);
								L_Call_Projectile_Detonated(projectile->type, projectile->owner_index, new_polygon_index, new_location);
								
								if (!film_profile.infinity_smg || (!(definition->flags&_penetrates_media_boundary) || !(flags&_projectile_hit_media)))
								{
									if ((definition->flags&_persistent_and_virulent) && !destroy_persistent_projectile && monster_obstruction_index!=NONE)
									{
										bool reassign_projectile = true;
										if (film_profile.prevent_dead_projectile_owners)
										{
											monster_data *monster = get_monster_data(monster_obstruction_index);
											reassign_projectile = MONSTER_IS_PLAYER(monster) || !MONSTER_IS_DYING(monster);
										}
										if (reassign_projectile)
											projectile->owner_index= monster_obstruction_index; /* keep going, but donÕt hit this target again */
									}
									// LP addition: don't remove a projectile that will hit media and continue (PMB flag)
									else if (!will_go_through)
									{
										remove_projectile(projectile_index);
									}
								}
								else if (film_profile.infinity_smg)
								{
									SET_PROJECTILE_CROSSED_MEDIA_BOUNDARY_STATUS(projectile, true);
								}
							}
						}
					}
					// Move the projectile if it hit nothing or it will go through media surface
					if (!(flags&_projectile_hit) || will_go_through)
					// else
					{
						/* move to the new_polygon_index */
						translate_map_object(projectile->object_index, &new_location, new_polygon_index);
						
						/* should we leave a contrail at our old location? */
						if ((projectile->ticks_since_last_contrail+=1)>=definition->ticks_between_contrails)
						{
							if (definition->maximum_contrails==NONE || projectile->contrail_count<definition->maximum_contrails)
							{
								projectile->contrail_count+= 1;
								projectile->ticks_since_last_contrail= 0;
								if (definition->contrail_effect!=NONE) new_effect(&old_location, old_polygon_index, definition->contrail_effect, object->facing);
							}
						}
		
						if ((flags&_flyby_of_current_player) && !PROJECTILE_HAS_MADE_A_FLYBY(projectile))
						{
							SET_PROJECTILE_FLYBY_STATUS(projectile, true);
							play_object_sound(projectile->object_index, definition->flyby_sound);
						}
		
						/* if we have a maximum range and we have exceeded it then remove the projectile */
						if (definition->maximum_range!=NONE)
						{
							if ((projectile->distance_travelled+= speed)>=definition->maximum_range)
							{
								remove_projectile(projectile_index);
							}
						}
					}
				}
			}
		}
Пример #16
0
static void	change_panel_state(
	short player_index,
	short panel_side_index)
{
	short state, make_sound= FALSE;
	struct side_data *side= get_side_data(panel_side_index);
	struct player_data *player= get_player_data(player_index);
	struct control_panel_definition *definition= get_control_panel_definition(side->control_panel_type);

	state= GET_CONTROL_PANEL_STATUS(side);
	
	/* Do the right thing, based on the panel type.. */
	switch (definition->panel_class)
	{
		case _panel_is_oxygen_refuel:
		case _panel_is_shield_refuel:
		case _panel_is_double_shield_refuel:
		case _panel_is_triple_shield_refuel:
#ifndef VULCAN
			player->control_panel_side_index= player->control_panel_side_index==panel_side_index ? NONE : panel_side_index;
			state= get_recharge_status(panel_side_index);
			SET_CONTROL_PANEL_STATUS(side, state);
			if (!state) set_control_panel_texture(side);
#endif
			break;
		case _panel_is_computer_terminal:
#ifndef VULCAN
			if (get_game_state()==_game_in_progress && !PLAYER_HAS_CHEATED(player) && !PLAYER_HAS_MAP_OPEN(player))
			{
				/* this will handle changing levels, if necessary (i.e., if weÕre finished) */
				enter_computer_interface(player_index, side->control_panel_permutation, calculate_level_completion_state());
			}
#endif
			break;
		case _panel_is_tag_switch:
			if (definition->item==NONE || (!state && try_and_subtract_player_item(player_index, definition->item)))
			{
				state= !state;
				make_sound= set_tagged_light_statuses(side->control_panel_permutation, state);
				if (try_and_change_tagged_platform_states(side->control_panel_permutation, state)) make_sound= TRUE;
				if (!side->control_panel_permutation) make_sound= TRUE;
				if (make_sound)
				{
					SET_CONTROL_PANEL_STATUS(side, state);
					set_control_panel_texture(side);
				}
			}
			break;
		case _panel_is_light_switch:
			state= !state;
			make_sound= set_light_status(side->control_panel_permutation, state);
			break;
		case _panel_is_platform_switch:
			state= !state;
			make_sound= try_and_change_platform_state(get_polygon_data(side->control_panel_permutation)->permutation, state);
			break;
		case _panel_is_pattern_buffer:
#ifndef VULCAN
			if (dynamic_world->tick_count-player->ticks_at_last_successful_save>MINIMUM_RESAVE_TICKS &&
				player_controlling_game() && !PLAYER_HAS_CHEATED(local_player) && !game_is_networked)
			{
				play_control_panel_sound(panel_side_index, _activating_sound);
//				fade_out_background_music(30);

				/* Assume a successful save- prevents vidding of the save game key.. */
				player->ticks_at_last_successful_save= dynamic_world->tick_count;
				if (!save_game()) 
				{
					// AMR 3/12/97 vidding happens with InputSprocket with this here
					//player->ticks_at_last_successful_save= 0;
				}
//				fade_in_background_music(30);
			}
#endif
			break;

		default:
			halt();
	}
	
	if (make_sound)
	{
		play_control_panel_sound(panel_side_index, state ? _activating_sound : _deactivating_sound);
	}
	
	return;	
}
Пример #17
0
/* returns next polygon index or NONE if there are no more polygons left cheaper than maximum_cost */
short flood_map(
	short first_polygon_index,
	long maximum_cost,
	cost_proc_ptr cost_proc,
	short flood_mode,
	void *caller_data)
{
	short lowest_cost_node_index, node_index;
	struct node_data *node;
	short polygon_index;
	long lowest_cost;

	/* initialize ourselves if first_polygon_index!=NONE */
	if (first_polygon_index!=NONE)
	{
		/* clear the visited polygon array */
		memset(visited_polygons, NONE, sizeof(short)*MAXIMUM_POLYGONS_PER_MAP);
		
		node_count= 0;
		last_node_index_expanded= NONE;
		add_node(NONE, first_polygon_index, 0, 0, (flood_mode==_flagged_breadth_first) ? *((long*)caller_data) : 0);
	}
	
	switch (flood_mode)
	{
		case _best_first:
			/* find the unexpanded node with the lowest cost */
			lowest_cost= maximum_cost, lowest_cost_node_index= NONE;
			for (node= nodes, node_index= 0; node_index<node_count; ++node_index, ++node)
			{
				if (NODE_IS_UNEXPANDED(node)&&node->cost<lowest_cost)
				{
					lowest_cost_node_index= node_index;
					lowest_cost= node->cost;
				}
			}
			break;
		
		case _breadth_first:
		case _flagged_breadth_first:
			/* find the next unexpanded node in the list under maximum_cost */
			node_index= (last_node_index_expanded==NONE) ? 0 : (last_node_index_expanded+1);
			for (node= nodes+node_index; node_index<node_count; ++node_index, ++node)
			{
				if (node->cost<maximum_cost) break;
			}
			if (node_index==node_count)
			{
				lowest_cost_node_index= NONE;
				lowest_cost= maximum_cost;
			}
			else
			{
				lowest_cost_node_index= node_index;
				lowest_cost= node->cost;
			}
			break;
		
		case _depth_first:
			/* implementation left to the caller (c.f., zen() in fareast.c) */
			halt();
			
		default:
			halt();
	}

	/* if we found a node, mark it as expanded and add itÕs adjacent non-solid polygons to the search tree */
	if (lowest_cost_node_index!=NONE)
	{
		struct polygon_data *polygon;
		short i;
		
		/* for flood_depth() and reverse_flood_map(), remember which node we successfully expanded last */
		last_node_index_expanded= lowest_cost_node_index;

		/* get pointer to lowest cost node */
		assert(lowest_cost_node_index>=0&&lowest_cost_node_index<node_count);
		node= nodes+lowest_cost_node_index;

		polygon= get_polygon_data(node->polygon_index);
		assert(!POLYGON_IS_DETACHED(polygon));

		/* mark node as expanded */
		MARK_NODE_AS_EXPANDED(node);

		for (i= 0; i<polygon->vertex_count; ++i)		
		{
			short destination_polygon_index= polygon->adjacent_polygon_indexes[i];
			
			if (destination_polygon_index!=NONE &&
				(maximum_cost!=LONG_MAX || visited_polygons[destination_polygon_index]==UNVISITED))
			{
				long new_user_flags= node->user_flags;
				long cost= cost_proc ? cost_proc(node->polygon_index, polygon->line_indexes[i], destination_polygon_index, (flood_mode==_flagged_breadth_first) ? &new_user_flags : caller_data) : polygon->area;
				
				/* polygons with zero or negative costs are not added to the node list */
				if (cost>0) add_node(lowest_cost_node_index, destination_polygon_index, node->depth+1, lowest_cost+cost, new_user_flags);
			}
		}
		
		polygon_index= node->polygon_index;
		if (flood_mode==_flagged_breadth_first) *((long*)caller_data)= node->user_flags;
	}
	else
	{
		polygon_index= NONE;
	}
	
	return polygon_index;
}
Пример #18
0
bool try_and_add_player_item(short player_index, short type) 
{
	item_definition *definition = get_item_definition(type);
	// LP change: added idiot-proofing
	if (!definition) 
		return false;
	
	player_data *player = get_player_data(player_index);
	short grabbed_sound_index = NONE;
	bool success = false;

	switch (definition->item_kind)
	{
		case _powerup: /* powerups donÕt get added to your inventory */
			if (legal_player_powerup(player_index, type))
			{
				process_player_powerup(player_index, type);
				object_was_just_destroyed(_object_is_item, type);
				grabbed_sound_index= Sound_GotPowerup();
				success= true;
			}
			break;
		
		case _ball:
			// START Benad
			/* Note that you can only carry ONE ball (ever) */
			if(find_player_ball_color(player_index)==NONE)
			{
				player_data *player= get_player_data(player_index);
				
				// When taking ball of your own team, it returns to its original
				// position on the map, unless it's already in our base (or hill).
				if ( (GET_GAME_TYPE() == _game_of_capture_the_flag) &&
					 (type - BALL_ITEM_BASE == player->team)  )
				{
					// START Benad modified oct. 1st
					polygon_data *polygon= get_polygon_data(player->supporting_polygon_index);
					if (polygon->type!=_polygon_is_base)
					{
						object_was_just_destroyed(_object_is_item, type);
						grabbed_sound_index= Sound_GotItem();
						success= true;
						goto DONE;
					}
					else // _polygon_is_base and base == player->team
						 // base != player->team taken care of in update_net_game
						 // (your ball should NEVER get there)
					{
						success= false;
						goto DONE;
					}
					// END Benad modified oct. 1st
				}
				else if (GET_GAME_TYPE() == _game_of_rugby)
				{
					// ghs: work around for SF 2894880

					// if you're in an enemy base
					// and pick up the ball, you
					// score
					polygon_data* polygon = get_polygon_data(player->supporting_polygon_index);
					if (polygon->type == _polygon_is_base && polygon->permutation != player->team)
					{
						/* Goal! */

						// defined in network_games.cpp
						const int _points_scored = 0;
						player->netgame_parameters[_points_scored]++;
						team_netgame_parameters[player->team][_points_scored]++;
						object_was_just_destroyed(_object_is_item, type);
						grabbed_sound_index = Sound_GotItem();
						success = true;
						goto DONE;
					}
				}
				
				player->items[type]= 1;

				// OK, since only for loading weapon. Ignores item_type, cares
				// only about item_kind (here, _ball).
				/* Load the ball weapon.. */
				process_new_item_for_reloading(player_index, _i_red_ball);
				
				/* Tell the interface to redraw next time it has to */
				mark_player_inventory_as_dirty(player_index, type);
				
				success= true;
			}
			grabbed_sound_index= NONE;
			break;
			// END Benad
					
		case _weapon:
		case _ammunition:
		case _item:
			/* Increment the count */	
			assert(type>=0 && type<NUMBER_OF_ITEMS);
			if(player->items[type]==NONE)
			{
				/* just got the first one.. */
				player->items[type]= 1;
				success= true;
			} 
			else if(player->items[type]+1<=definition->maximum_count_per_player ||
				(dynamic_world->game_information.difficulty_level==_total_carnage_level && ((static_world->environment_flags & _environment_m1_weapon_pickups) || definition->item_kind==_ammunition)))
			{
				/* Increment your count.. */
				player->items[type]++;
				success= true;
			} else {
				/* You have exceeded the count of these items */
			}

			grabbed_sound_index= Sound_GotItem();

			if(success)
			{
				/* Reload or whatever.. */
				process_new_item_for_reloading(player_index, type);
					
				/* Tell the interface to redraw next time it has to */
				mark_player_inventory_as_dirty(player_index, type);
			}
			break;
		
		default:
			assert(false);
			break;
	}
	// Benad. Burk.
	DONE:
	
	//CP Addition: call any script traps available
	// jkvw: but only if we actually got the item
	if (success)
	{
		//MH: Call Lua script hook
		L_Call_Got_Item(type, player_index);
	}

	/* Play the pickup sound */
	if (success && player_index==current_player_index)
	{
		SoundManager::instance()->PlayLocalSound(grabbed_sound_index);
	
		/* Flash screen */
		start_fade(_fade_bonus);
	}

	return success;
}
// LP change: make it better able to do long-distance views
uint16 RenderVisTreeClass::decide_where_vertex_leads(
	short *polygon_index,
	short *line_index,
	short *side_index,
	short endpoint_index_in_polygon_list,
	world_point2d *origin,
	long_vector2d *_vector, // world_vector2d *vector,
	uint16 clip_flags,
	short bias)
{
	polygon_data *polygon= get_polygon_data(*polygon_index);
	short endpoint_index= polygon->endpoint_indexes[endpoint_index_in_polygon_list];
	short index;
	
	switch (bias)
	{
		case _no_bias:
//			dprintf("splitting at endpoint #%d", endpoint_index);
			clip_flags|= _split_render_ray;
			*polygon_index= *line_index= *side_index= NONE;
			index= NONE;
			break;
		
		case _clockwise_bias:
			index= endpoint_index_in_polygon_list;
			break;
		
		case _counterclockwise_bias:
			index= WRAP_LOW(endpoint_index_in_polygon_list, polygon->vertex_count-1);
			break;
		
		default:
			// LP change:
			assert(false);
			// halt();
	}
	
	if (index!=NONE)
	{
		line_data *line;
		world_point2d *vertex;
		CROSSPROD_TYPE cross_product;

		*line_index= polygon->line_indexes[index];
		*side_index= polygon->side_indexes[index];
		*polygon_index= polygon->adjacent_polygon_indexes[index];
		
		line= get_line_data(*line_index);
		if (*polygon_index!=NONE && LINE_IS_TRANSPARENT(line))
		{
			polygon= get_polygon_data(*polygon_index);
			
			/* locate our endpoint in this polygon */
			for (index=0;
					index<polygon->vertex_count && polygon->endpoint_indexes[index]!=endpoint_index;
					++index)
				;
			vassert(index!=polygon->vertex_count, csprintf(temporary, "endpoint #%d not in polygon #%d", endpoint_index, *polygon_index));
	
			switch (bias)
			{
				case _clockwise_bias: index= WRAP_HIGH(index, polygon->vertex_count-1); break;
				case _counterclockwise_bias: index= WRAP_LOW(index, polygon->vertex_count-1); break;
				default:
					assert(false);
					break;
			}
			
			vertex= &get_endpoint_data(polygon->endpoint_indexes[index])->vertex;
			// LP change: made more long-distance-friendly
			cross_product= CROSSPROD_TYPE(long(vertex->x)-long(origin->x))*_vector->j - CROSSPROD_TYPE(long(vertex->y)-long(origin->y))*_vector->i;
			
			if ((bias==_clockwise_bias&&cross_product>=0) || (bias==_counterclockwise_bias&&cross_product<=0))
			{
				/* weÕre leaving this endpoint, set clip flag in case itÕs solid */
				clip_flags|= (bias==_clockwise_bias) ? _clip_left : _clip_right;
			}
		}

//		dprintf("left endpoint #%d via line #%d to polygon #%d (bias==#%d)", endpoint_index, *line_index, *polygon_index, bias);
	}

	return clip_flags;
}
Пример #20
0
// Main routine
void RenderVisTreeClass::build_render_tree()
{
	assert(view);	// Idiot-proofing

	/* initialize the queue where we remember polygons we need to fire at */
	initialize_polygon_queue();

	/* initialize our node list to contain the root, etc. */
	initialize_render_tree();
	
	/* reset clipping buffers */
	initialize_clip_data();
	
	// LP change:
	// Adjusted for long-vector handling
	// Using start index of list of nodes: 0
	long_vector2d view_edge;
	
	short_to_long_2d( view->left_edge, view_edge );
	cast_render_ray( &view_edge, NONE, &Nodes.front(), _counterclockwise_bias );
	
	short_to_long_2d( view->right_edge, view_edge );
	cast_render_ray( &view_edge, NONE, &Nodes.front(), _clockwise_bias );
	
	/* 
		pull polygons off the queue, fire at all their new endpoints, building the tree as we go 
	*/
	while (polygon_queue_size)
	{
		auto polygon_index 	= PolygonQueue[ --polygon_queue_size ];
		polygon_data *polygon 	= get_polygon_data(polygon_index);
		
		assert( !POLYGON_IS_DETACHED(polygon) );
		
		const ix vertex_count = polygon->vertex_count;
		for( ix vertex_index = 0; vertex_index < vertex_count; ++vertex_index )
		{
			const auto endpoint_index	= polygon->endpoint_indexes[vertex_index];
			endpoint_data *endpoint		= get_endpoint_data(endpoint_index);
			
			if (TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_visited))
				continue;
			// LP change: move toward correct handling of long distances
			long_vector2d _vector;
			
			/* transform all visited endpoints */
			endpoint->transformed = endpoint->vertex;
			transform_overflow_point2d( &endpoint->transformed, 
						(world_point2d *) &view->origin, 
						view->yaw, 
						&endpoint->flags );
						
			/* calculate an outbound vector to this endpoint */
			// LP: changed to do long distance correctly.	
			_vector.i 	= int32( endpoint->vertex.x ) - int32( view->origin.x );
			_vector.j	= int32( endpoint->vertex.y ) - int32( view->origin.y );
			
			// LP change: compose a true transformed point to replace endpoint->transformed,
			// and use it in the upcoming code
			long_vector2d transformed_endpoint;
			overflow_short_to_long_2d( 	endpoint->transformed, 
							endpoint->flags, 
							transformed_endpoint );
			if (transformed_endpoint.i > 0)
			{
				const int32 x = view->half_screen_width + 
				( transformed_endpoint.j * view->world_to_screen_x ) / transformed_endpoint.i;
				
				endpoint_x_coordinates[ endpoint_index ] = 
					static_cast< int16 >( PIN(x, INT16_MIN, INT16_MAX) );
					
				SET_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed);
			}
			
			/* 
				do two cross products to determine whether this endpoint is in our view cone or not
				(we don't have to cast at points outside the cone) 
			*/
			const auto ri = view->right_edge.i;
			const auto rj = view->right_edge.j;
			const int32 crossprod_right = ( ri * _vector.j ) - ( rj * _vector.i );
			
			const auto li = view->left_edge.i;
			const auto lj = view->left_edge.j;
			const int32 crossprod_left = ( li * _vector.j ) - ( lj * _vector.i );
			
			if( crossprod_right <= 0 && crossprod_left >= 0 )
			{
				//it's in our view, cast at it
				int16 endpoint_;
				
				if( ENDPOINT_IS_TRANSPARENT(endpoint) )
					endpoint_ = NONE;
				else
					endpoint_ = endpoint_index;
					
				cast_render_ray(&_vector, endpoint_, &Nodes.front(), _no_bias);
			}
			SET_RENDER_FLAG(endpoint_index, _endpoint_has_been_visited);
		
		}
	}
}
Пример #21
0
// LP change: make it better able to do long-distance views
uint16 RenderVisTreeClass::next_polygon_along_line(int16 * polygon_index,
	world_point2d *origin, /* not necessairly in polygon_index */
	long_vector2d *_vector, // world_vector2d *vector,
	int16 *clipping_endpoint_index, /* if non-NONE on entry this is the solid endpoint weÕre shooting for */
	int16 *clipping_line_index, /* NONE on exit if this polygon transition wasnÕt accross an elevation line */
	int16 bias)
{
	polygon_data *polygon	= get_polygon_data(*polygon_index);
	int16 next_polygon_index, crossed_line_index, crossed_side_index;
	bool passed_through_solid_vertex = false;
	uint16 clip_flags = 0;


	ADD_POLYGON_TO_AUTOMAP(*polygon_index);
	PUSH_POLYGON_INDEX(*polygon_index);

	int16 state = _looking_for_first_nonzero_vertex;
	int16 vertex_index = 0;
	int16 vertex_delta = 1; /* start searching clockwise from vertex zero */
	
	// LP change: added test for looping around:
	// will remember the first vertex examined when the state has changed
	auto initial_vertex_index = vertex_index;
	
	bool changed_state = true;
	
	do
	{
		// Jump out of loop?
		if (changed_state)
			changed_state = false;
		else if (vertex_index == initial_vertex_index)
		{
			// Attempt to idiot-proof it by returning nothing
			next_polygon_index = NONE;
			crossed_line_index = NONE;
			crossed_side_index = NONE;
			break;
		}
			
		auto endpoint_index = polygon->endpoint_indexes[vertex_index];
		world_point2d *vertex = &get_endpoint_data(endpoint_index)->vertex;
		// LP change to make it more long-distance-friendly
		
		//urghhhhhhhhhhh
		CROSSPROD_TYPE cross_product	= 
			CROSSPROD_TYPE(int32(vertex->x)-int32(origin->x))*_vector->j 
			- 
			CROSSPROD_TYPE(int32(vertex->y)-int32(origin->y))*_vector->i;
		
		if (cross_product < 0)
		{
		    switch (state)
		    {
			case _looking_for_first_nonzero_vertex:
			    /* search counterclockwise for transition (right to left) */
			    state = _looking_counterclockwise_for_left_vertex;
			    vertex_delta = -1;
			    // LP change: resetting loop test
			    initial_vertex_index = vertex_index;
			    changed_state = true;
			    break;

			case _looking_clockwise_for_right_vertex: /* found the transition we were looking for */
			{
			    ix i = WRAP_LOW(vertex_index, polygon->vertex_count-1);
			    next_polygon_index = polygon->adjacent_polygon_indexes[i];
			    crossed_line_index = polygon->line_indexes[i];
			    crossed_side_index = polygon->side_indexes[i];
			}
			case _looking_for_next_nonzero_vertex: /* next_polygon_index already set */
			    state = NONE;
			    break;
		    }
		} 
		else if (cross_product > 0)
		{
		    switch (state)
		    {
			case _looking_for_first_nonzero_vertex:
			    /* search clockwise for transition (left to right) */
			    state= _looking_clockwise_for_right_vertex;
			    // LP change: resetting loop test
			    initial_vertex_index = vertex_index;
			    changed_state = true;
			    break;

			case _looking_counterclockwise_for_left_vertex: /* found the transition we were looking for */
			    next_polygon_index = polygon->adjacent_polygon_indexes[vertex_index];
			    crossed_line_index = polygon->line_indexes[vertex_index];
			    crossed_side_index = polygon->side_indexes[vertex_index];
			case _looking_for_next_nonzero_vertex: /* next_polygon_index already set */
			    state = NONE;
			    break;
		    }
		} 
		else
		{
		    if (state!=_looking_for_first_nonzero_vertex)
		    {
			if (endpoint_index==*clipping_endpoint_index) 
				passed_through_solid_vertex = true;

			/* 
				if we think we know what's on the other side of this zero (these zeros)
				change the state: if we don't find what we're looking for then the polygon
				is entirely on one side of the line or the other (except for this vertex),
				in any case we need to call decide_where_vertex_leads() to find out what's
				on the other side of this vertex 
			*/
			switch (state)
			{
			    case _looking_clockwise_for_right_vertex:
			    case _looking_counterclockwise_for_left_vertex:
				next_polygon_index= *polygon_index;
				clip_flags|= decide_where_vertex_leads(&next_polygon_index, &crossed_line_index, &crossed_side_index,
					   vertex_index, origin, _vector, clip_flags, bias);
				state = _looking_for_next_nonzero_vertex;
				// LP change: resetting loop test
				initial_vertex_index = vertex_index;
				changed_state = true;
				break;
			}
		    }
		}
		/* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) */
		vertex_index = vertex_delta < 0 ? WRAP_LOW(vertex_index, polygon->vertex_count - 1) :
			WRAP_HIGH(vertex_index, polygon->vertex_count - 1);
	}
	while (state!=NONE);


	/* 
		if we didn't pass through the solid vertex we were aiming for, set clipping_endpoint_index to NONE,
		we assume the line we passed through doesn't clip, and set clipping_line_index to NONE
		(this will be corrected in a few moments if we chose poorly) 
	*/
	if (!passed_through_solid_vertex) 
		*clipping_endpoint_index = NONE;
		
	*clipping_line_index = NONE;
	
	if (crossed_line_index==NONE)
	{
		*polygon_index = next_polygon_index;
		return clip_flags;
	}

	const line_data *restrict line = get_line_data(crossed_line_index);

	/* add the line we crossed to the automap */
	ADD_LINE_TO_AUTOMAP(crossed_line_index);

	/* if the line has a side facing this polygon, mark the side as visible */
	if (crossed_side_index!=NONE) 
		SET_RENDER_FLAG(crossed_side_index, _side_is_visible);

	/* if this line is transparent we need to check for a change in elevation for clipping,
		if itÕs not transparent then we canÕt pass through it */
	// LP change: added test for there being a polygon on the other side
	if (LINE_IS_TRANSPARENT(line) && next_polygon_index != NONE)
	{
		const polygon_data *restrict next_polygon = get_polygon_data(next_polygon_index);
		
		if (line->highest_adjacent_floor > next_polygon->floor_height ||
			line->highest_adjacent_floor > polygon->floor_height) 
				clip_flags |= _clip_down; /* next polygon floor is lower */
				
		if (line->lowest_adjacent_ceiling < next_polygon->ceiling_height ||
			line->lowest_adjacent_ceiling < polygon->ceiling_height) 
				clip_flags |= _clip_up; /* next polygon ceiling is higher */
				
		if ( clip_flags&(_clip_up|_clip_down) ) 
			*clipping_line_index = crossed_line_index;
	}
	else
/* be sure to examine all of a nodeÕs parents for clipping information (gak!) */
clipping_window_data *RenderSortPolyClass::build_clipping_windows(
	node_data *ChainBegin)
{
	// LP change: growable lists
	AccumulatedLineClips.clear();
	AccumulatedEndpointClips.clear();
	clipping_window_data *first_clipping_window = NULL;
	clipping_window_data *last_clipping_window = NULL;
	endpoint_clip_data *endpoint;
	line_clip_data *line;
	short x0, x1; /* ignoring what clipping parameters weÕve gotten, this is the left and right borders of this node on the screen */
	short i, j;

	// LP: references to simplify the code
	vector<endpoint_clip_data>& EndpointClips = RVPtr->EndpointClips;
	vector<line_clip_data>& LineClips = RVPtr->LineClips;
	vector<clipping_window_data>& ClippingWindows = RVPtr->ClippingWindows;
	vector<short>& endpoint_x_coordinates = RVPtr->endpoint_x_coordinates;
	
	/* calculate x0,x1 (real left and right borders of this node) in case the left and right borders
		of the window are sloppy */
	{
		// LP change: look at beginning of chain
		polygon_data *polygon= get_polygon_data(ChainBegin->polygon_index); /* all these nodes should be the same */
		
		x0= SHRT_MAX, x1= SHRT_MIN;
		for (i= 0;i<polygon->vertex_count;++i)
		{
			short endpoint_index= polygon->endpoint_indexes[i];
			
			if (TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed))
			{
				short x= endpoint_x_coordinates[endpoint_index];
				
				if (x<x0) x0= x;
				if (x>x1) x1= x;
			}
			else
			{
				x0= SHRT_MIN, x1= SHRT_MAX;
				break;
			}
		}
	}
	
	/* add left, top and bottom of screen */
	endpoint_clip_data *EndpointClipPtr = &EndpointClips[indexLEFT_SIDE_OF_SCREEN];
	AccumulatedEndpointClips.push_back(EndpointClipPtr);
	line_clip_data *LineClipPtr = &LineClips[indexTOP_AND_BOTTOM_OF_SCREEN];
	AccumulatedLineClips.push_back(LineClipPtr);

	/* accumulate clipping information, left to right, into local arrays */
	// Move along chain
	for (node_data *ChainNode = ChainBegin; ChainNode; ChainNode = ChainNode->PS_Shared)
	{
		node_data *node;
		
		// LP change: use chain node as starting point
		for (node= ChainNode;node;node= node->parent) /* examine this node and all parents! */
		{
			/* sort in endpoint clips (left to right) */
			for (i= 0;i<node->clipping_endpoint_count;++i)
			{
				endpoint= &EndpointClips[node->clipping_endpoints[i]];
				
				for (j= 0;j<short(AccumulatedEndpointClips.size());++j)
				{
					if (AccumulatedEndpointClips[j]==endpoint) { j= NONE; break; } /* found duplicate */
					if ((AccumulatedEndpointClips[j]->x==endpoint->x&&endpoint->flags==_clip_left) ||
						AccumulatedEndpointClips[j]->x>endpoint->x)
					{
						break; /* found sorting position if x is greater or x is equal and this is a left clip */
					}
				}
				
				if (j!=NONE) /* if the endpoint was not a duplicate */
				{
					/* expand the array, if necessary, and add the new endpoint */
					int Length = AccumulatedEndpointClips.size();
					AccumulatedEndpointClips.push_back(NULL);
					assert(AccumulatedEndpointClips.size() <= 32767);		// Originally a short value
					if (j!=Length) memmove(&AccumulatedEndpointClips[j+1], &AccumulatedEndpointClips[j],
						(Length-j)*sizeof(endpoint_clip_data *));
					AccumulatedEndpointClips[j]= endpoint;
				}
			}

			/* sort in line clips, avoiding redundancies;  calculate_vertical_line_clip_data(),
				the function which deals with these, does not depend on them being sorted */
			for (i= 0;i<node->clipping_line_count;++i)
			{
				line= &LineClips[node->clipping_lines[i]];
				
				for (j= 0;j<short(AccumulatedLineClips.size());++j) if (AccumulatedLineClips[j]==line) break; /* found duplicate */
				if (j==short(AccumulatedLineClips.size())) /* if the line was not a duplicate */
				{
					AccumulatedLineClips.push_back(line);
					assert(AccumulatedLineClips.size() <= 32767);		// Originally a short value
				}
			}
		}
	}
	
//	dprintf("#%d accumulated points @ %p", accumulated_endpoint_clip_count, accumulated_endpoint_clips);
//	dprintf("#%d accumulated lines @ %p", accumulated_line_clip_count, accumulated_line_clips);

	/* add right side of screen */
	EndpointClipPtr = &EndpointClips[indexRIGHT_SIDE_OF_SCREEN];
	AccumulatedEndpointClips.push_back(EndpointClipPtr);

	/* build the clipping windows */
	{
		short state= _looking_for_left_clip;
		endpoint_clip_data *left_clip = NULL, *right_clip = NULL;

		for (i= 0;i<short(AccumulatedEndpointClips.size());++i)
		{
			endpoint= AccumulatedEndpointClips[i];
	
			switch (endpoint->flags)
			{
				case _clip_left:
					switch (state)
					{
						case _looking_for_left_clip:
							left_clip= endpoint;
							state= _looking_for_right_clip;
							break;
						case _looking_for_right_clip:
							left_clip= endpoint; /* found more strict clipping point, use it instead */
							break;
					}
					break;
				
				case _clip_right:
					switch (state)
					{
						case _looking_for_right_clip:
							right_clip= endpoint;
							state= _building_clip_window;
							break;
						
						/* ignore _left_clips */
					}
					break;
				
				default:
					vassert(false,csprintf(temporary,"RenderSortPoly.cpp: build_clipping_windows(): bad state: %d",state));
					break;
			}

			if (state==_building_clip_window)
			{
				if (left_clip->x<view->screen_width && right_clip->x>0 && left_clip->x<right_clip->x)
				{
					// LP change: clipping windows are in growable list
					size_t Length = ClippingWindows.size();
					POINTER_DATA OldCWPointer = POINTER_CAST(&ClippingWindows.front());
					
					// Add a dummy object and check if the pointer got changed
					clipping_window_data Dummy;
					Dummy.next_window = NULL;			// Fake initialization to shut up CW
					ClippingWindows.push_back(Dummy);
					POINTER_DATA NewCWPointer = POINTER_CAST(&ClippingWindows.front());
				
					if (NewCWPointer != OldCWPointer)
					{
						// Get the clipping windows and sorted nodes into sync; no render objects yet
						for (size_t k=0; k<Length; k++)
						{
							clipping_window_data &ClippingWindow = ClippingWindows[k];
							if (ClippingWindow.next_window != NULL)
								ClippingWindow.next_window = (clipping_window_data *)(NewCWPointer + (POINTER_CAST(ClippingWindow.next_window) - OldCWPointer));
						}
						for (size_t k=0; k<SortedNodes.size(); k++)
						{
							sorted_node_data &SortedNode = SortedNodes[k];
							if (SortedNode.clipping_windows != NULL)
								SortedNode.clipping_windows = (clipping_window_data *)(NewCWPointer + (POINTER_CAST(SortedNode.clipping_windows) - OldCWPointer));
						}
					}
					clipping_window_data *window= &ClippingWindows[Length];
					
					/* handle maintaining the linked list of clipping windows */
					if (!first_clipping_window)
					{
						first_clipping_window= last_clipping_window= window;
					}
					else
					{
						last_clipping_window->next_window= window;
						last_clipping_window= window;
					}
					
					window->x0= left_clip->x, window->x1= right_clip->x;
					window->left= left_clip->vector;
					window->right= right_clip->vector;
					calculate_vertical_clip_data(&AccumulatedLineClips.front(), AccumulatedLineClips.size(), window,
						MAX(x0, window->x0), MIN(x1, window->x1));
					window->next_window= NULL;
				}
				
				state= _looking_for_left_clip;
			}
		}
	}

	return first_clipping_window;
}
Пример #23
0
static void a1_swipe_nearby_items(short player_index)
{
	object_data *object;
	object_data *player_object;
	player_data *player = get_player_data(player_index);
	short next_object;
	polygon_data *polygon;
	short *neighbor_indexes;
	short i;

	player_object = get_object_data(get_monster_data(player->monster_index)->object_index);

	polygon= get_polygon_data(player_object->polygon);
	neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
	
	// Skip this step if neighbor indexes were not found
	if (!neighbor_indexes) return;

	for (i=0;i<polygon->neighbor_count;++i)
	{	
		
		struct polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
		
		/*
			LP change: since precalculate_map_indexes() and its associated routine
			intersecting_flood_proc() appear to have some bugs in them, I will
			instead search the neighbors of each indexed polygon.
			
			Starting the search from -1 is a kludge designed to include a search
			for the current polygon.
		*/
		polygon_data *source_polygon = neighboring_polygon;
		for (int ngbr_indx = -1; ngbr_indx<source_polygon->vertex_count; ngbr_indx++)
		{
		if (ngbr_indx >= 0)
		{
			// Be sure to check on whether there is a valid polygon on the other side
			auto adjacent_index = source_polygon->adjacent_polygon_indexes[ngbr_indx];
			if (adjacent_index == NONE) continue;
			neighboring_polygon = get_polygon_data(adjacent_index);
		}
		else
			neighboring_polygon = source_polygon;
		
		if (!POLYGON_IS_DETACHED(neighboring_polygon))
		{
			next_object= neighboring_polygon->first_object;

			while(next_object != NONE)
			{
				object= get_object_data(next_object);
				if (GET_OBJECT_OWNER(object)==_object_is_item && !OBJECT_IS_INVISIBLE(object)) 
				{
					if (guess_distance2d((world_point2d *) &player->location, (world_point2d *) &object->location)<=MAXIMUM_ARM_REACH)
					{
						world_distance radius, height;
						
						get_monster_dimensions(player->monster_index, &radius, &height);
		
						if (object->location.z >= player->location.z - MAXIMUM_ARM_REACH && object->location.z <= player->location.z + height &&
							test_item_retrieval(player_object->polygon, &player_object->location, &object->location))
						{
							if(get_item(player_index, next_object))
							{
								/* Start the search again.. */
								next_object= neighboring_polygon->first_object;
								continue;
							}
						}
					}
				}
				
				next_object= object->next_object;
			}
		}
		// LP addition: end of that kludgy search loop
		}
	}
}