static int Lua_Monster_Get_Polygon(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    Lua_Polygon::Push(L, object->polygon);
    return 1;
}
static bool Lua_Scenery_Valid(int32 index)
{
	if (index < 0 || index >= MAXIMUM_OBJECTS_PER_MAP)
		return false;

	object_data *object = GetMemberWithBounds(objects, index, MAXIMUM_OBJECTS_PER_MAP);
	if (SLOT_IS_USED(object))
	{
		if (GET_OBJECT_OWNER(object) == _object_is_scenery) 
			return true;
		else if (GET_OBJECT_OWNER(object) == _object_is_normal)
		{
			// check to make sure it's not a player's legs or torso
			for (int player_index = 0; player_index < dynamic_world->player_count; player_index++)
			{
				player_data *player = get_player_data(player_index);
				monster_data *monster = get_monster_data(player->monster_index);
				if (monster->object_index == index) 
					return false;
				else
				{
					object_data *object = get_object_data(monster->object_index);
					if (object->parasitic_object == index)
						return false;
				}
			}

			return true;
		}
	}

	return false;
}
static int Lua_Monster_Get_Z(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    lua_pushnumber(L, (double) object->location.z / WORLD_ONE);
    return 1;
}
int Lua_Monster_Position(lua_State *L)
{
    if (!lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4))
        return luaL_error(L, "position: incorrect argument type");

    short polygon_index = 0;
    if (lua_isnumber(L, 5))
    {
        polygon_index = static_cast<int>(lua_tonumber(L, 5));
        if (!Lua_Polygon::Valid(polygon_index))
            return luaL_error(L, "position: invalid polygon index");
    }
    else if (Lua_Polygon::Is(L, 5))
    {
        polygon_index = Lua_Polygon::Index(L, 5);
    }
    else
        return luaL_error(L, "position: incorrect argument type");

    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    object->location.x = static_cast<int>(lua_tonumber(L, 2) * WORLD_ONE);
    object->location.y = static_cast<int>(lua_tonumber(L, 3) * WORLD_ONE);
    object->location.z = static_cast<int>(lua_tonumber(L, 4) * WORLD_ONE);

    if (polygon_index != object->polygon)
    {
        remove_object_from_polygon_object_list(monster->object_index);
        add_object_to_polygon_object_list(monster->object_index, polygon_index);
    }
    return 0;
}
static int Lua_Monster_Get_Facing(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    lua_pushnumber(L, (double) object->facing * AngleConvert);
    return 1;
}
int Lua_Monster_Damage(lua_State *L)
{
    if (!lua_isnumber(L, 2))
        return luaL_error(L, "damage: incorrect argument type");

    int damage_amount = static_cast<int>(lua_tonumber(L, 2));
    int damage_type = NONE;
    if (lua_gettop(L) == 3)
    {
        damage_type = Lua_DamageType::ToIndex(L, 3);
    }

    damage_definition damage;
    if (damage_type != NONE)
        damage.type = damage_type;
    else
        damage.type = _damage_fist;

    damage.base = damage_amount;
    damage.random = 0;
    damage.flags = 0;
    damage.scale = FIXED_ONE;

    int monster_index = Lua_Monster::Index(L, 1);
    monster_data *monster = get_monster_data(monster_index);
    damage_monster(monster_index, NONE, NONE, &(monster->sound_location), &damage, NONE);
    return 0;
}
int Lua_Monster_Play_Sound(lua_State *L)
{
    short sound_index = Lua_Sound::ToIndex(L, 2);
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    play_object_sound(monster->object_index, sound_index);
    return 0;
}
示例#8
0
// ZZZ: If not already in predictive mode, save off partial game-state for later restoration.
static void
enter_predictive_mode()
{
	if(sPredictedTicks == 0)
	{
		for(short i = 0; i < dynamic_world->player_count; i++)
		{
			sSavedPlayerData[i] = *get_player_data(i);
			if(sSavedPlayerData[i].monster_index != NONE)
			{
				sSavedPlayerMonsterData[i] = *get_monster_data(sSavedPlayerData[i].monster_index);
				if(sSavedPlayerMonsterData[i].object_index != NONE)
				{
					sSavedPlayerObjectData[i] = *get_object_data(sSavedPlayerMonsterData[i].object_index);
					sSavedPlayerObjectNextObject[i] = sSavedPlayerObjectData[i].next_object;
					if(sSavedPlayerObjectData[i].parasitic_object != NONE)
						sSavedPlayerParasiticObjectData[i] = *get_object_data(sSavedPlayerObjectData[i].parasitic_object);
				}
			}
		}
		
		// Sanity checking
		sSavedTickCount = dynamic_world->tick_count;
		sSavedRandomSeed = get_random_seed();
	}
}
static int Lua_Monster_Get_Visible(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    lua_pushboolean(L, OBJECT_IS_VISIBLE(object));
    return 1;
}
示例#10
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;
		}
	
	}
}
static int Lua_Monster_Set_Vertical_Velocity(lua_State *L)
{
    if (!lua_isnumber(L, 2))
        return luaL_error(L, "vertical_velocity: incorrect argument type");

    monster_data* monster = get_monster_data(Lua_Monster::Index(L, 1));
    monster->vertical_velocity = static_cast<int>(lua_tonumber(L, 2) * WORLD_ONE);
    return 0;
}
static int Lua_Monster_Get_Active(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    if (MONSTER_IS_PLAYER(monster))
        return luaL_error(L, "active: monster is a player");

    lua_pushboolean(L, MONSTER_IS_ACTIVE(monster));
    return 1;
}
static int Lua_Monster_Set_Vitality(lua_State *L)
{
    if (!lua_isnumber(L, 2))
        return luaL_error(L, "vitality: incorrect argument type");

    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    monster->vitality = static_cast<int>(lua_tonumber(L, 2));
    return 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;
}
static int Lua_Monster_Set_Facing(lua_State *L)
{
    if (!lua_isnumber(L, 2))
        return luaL_error(L, "facing: incorrect argument type");

    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    object_data *object = get_object_data(monster->object_index);
    object->facing = static_cast<int>(lua_tonumber(L, 2) / AngleConvert);
    return 0;
}
static int Lua_Monster_Get_Player(lua_State *L)
{
    int monster_index = Lua_Monster::Index(L, 1);
    monster_data *monster = get_monster_data(monster_index);
    if (MONSTER_IS_PLAYER(monster))
        Lua_Player::Push(L, monster_index_to_player_index(monster_index));
    else
        lua_pushnil(L);

    return 1;
}
示例#17
0
/* every other field in the player structure should be valid when this call is made */
void initialize_player_physics_variables(
	short player_index)
{
	struct player_data *player= get_player_data(player_index);
	struct monster_data *monster= get_monster_data(player->monster_index);
	struct object_data *object= get_object_data(monster->object_index);
	struct physics_variables *variables= &player->variables;
	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, 0);

//#ifdef DEBUG
	obj_set(*variables, 0x80);
//#endif
	
	variables->head_direction= 0;
	variables->adjusted_yaw= variables->direction= INTEGER_TO_FIXED(object->facing);
	variables->adjusted_pitch= variables->elevation= 0;
	variables->angular_velocity= variables->vertical_angular_velocity= 0;
	variables->velocity= 0, variables->perpendicular_velocity= 0;
	variables->position.x= WORLD_TO_FIXED(object->location.x);
	variables->position.y= WORLD_TO_FIXED(object->location.y);
	variables->position.z= WORLD_TO_FIXED(object->location.z);
	variables->last_position= variables->position;
	variables->last_direction= variables->direction;
	/* .floor_height, .ceiling_height and .media_height will be calculated by instantiate, below */

	variables->external_angular_velocity= 0;
	variables->external_velocity.i= variables->external_velocity.j= variables->external_velocity.k= 0;
	variables->actual_height= constants->height;
	
	variables->step_phase= 0;
	variables->step_amplitude= 0;
	
	variables->action= _player_stationary;
	variables->old_flags= variables->flags= 0; /* not recentering, not above ground, not below ground (i.e., on floor) */

	/* setup shadow variables in player_data structure */
	instantiate_physics_variables(get_physics_constants_for_model(static_world->physics_model, 0),
		&player->variables, player_index, true, true);

#ifdef DIVERGENCE_CHECK
	if (!saved_point_iterations)
	{
		saved_points= new world_point3d[SAVED_POINT_COUNT];
		saved_thetas= new angle[SAVED_POINT_COUNT];
	}
	saved_point_count= 0;
	saved_point_iterations+= 1;
	saved_divergence_warning= false;
#endif
}
int Lua_Monster_Accelerate(lua_State *L)
{
    if (!lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4))
        return luaL_error(L, "accelerate: incorrect argument type");

    short monster_index = Lua_Monster::Index(L, 1);
    monster_data *monster = get_monster_data(monster_index);
    (void)monster;
    double direction = static_cast<double>(lua_tonumber(L, 2));
    double velocity = static_cast<double>(lua_tonumber(L, 3));
    double vertical_velocity = static_cast<double>(lua_tonumber(L, 4));

    accelerate_monster(monster_index, static_cast<int>(vertical_velocity * WORLD_ONE), static_cast<int>(direction/AngleConvert), static_cast<int>(velocity * WORLD_ONE));
    return 0;
}
示例#19
0
文件: devices.c 项目: DrItanium/moo
static boolean line_is_within_range(
	short monster_index,
	short line_index,
	world_distance range)
{
	world_point3d monster_origin= get_object_data(get_monster_data(monster_index)->object_index)->location;
	world_point3d line_origin;
	world_distance radius, height;
	world_distance dx, dy, dz;
	
	calculate_line_midpoint(line_index, &line_origin);
	get_monster_dimensions(monster_index, &radius, &height);
	monster_origin.z+= height>>1;
	
	dx= monster_origin.x-line_origin.x;
	dy= monster_origin.y-line_origin.y;
	dz= 2*(monster_origin.z-line_origin.z); /* dz is weighted */
	
	return isqrt(dx*dx + dy*dy + dz*dz)<range ? TRUE : FALSE;
}
示例#20
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
}
static int Lua_Monster_Set_Visible(lua_State *L) {
    int monster_index = Lua_Monster::Index(L, 1);
    monster_data *monster = get_monster_data(monster_index);
    object_data *object = get_object_data(monster->object_index);
    int invisible = !lua_toboolean(L, 2);
    if(monster->action == _monster_is_teleporting_out) return 0;
    if(MONSTER_IS_ACTIVE(monster) || monster->vitality >= 0) {
        // Cool stuff happens if you just set an active
        // monster to invisible.  What we should do instead of
        // the below is expose teleports_out_when_deactivated
        /*if(invisible) {
          monster->flags |= (uint16)_monster_teleports_out_when_deactivated;
          deactivate_monster(monster_index);
          }*/
        return luaL_error(L, "visible: monster already activated");
    }
    else {
        // No real possibility of messing stuff up here.
        SET_OBJECT_INVISIBILITY(object, invisible);
    }
    return 0;
}
示例#22
0
void update_player_physics_variables(
	short player_index,
	uint32 action_flags,
	bool predictive)
{
	struct player_data *player= get_player_data(player_index);
	struct physics_variables *variables= &player->variables;
	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, action_flags);

	physics_update(constants, variables, player, action_flags);
	instantiate_physics_variables(constants, variables, player_index, false, !predictive);

#ifdef DIVERGENCE_CHECK
	if (saved_point_count<SAVED_POINT_COUNT)
	{
		struct object_data *object= get_object_data(get_monster_data(player->monster_index)->object_index);
		world_point3d p= object->location;
		world_point3d *q= saved_points+saved_point_count;
		angle *facing= saved_thetas+saved_point_count;
		
		if (saved_point_iterations==1)
		{
			saved_points[saved_point_count]= p;
			*facing= object->facing;
		}
		else
		{
			if (p.x!=q->x||p.y!=q->y||p.z!=q->z||*facing!=object->facing&&!saved_divergence_warning)
			{
				dprintf("divergence @ tick %d: (%d,%d,%d,%d)!=(%d,%d,%d,%d)", saved_point_count,
					q->x, q->y, q->z, *facing, p.x, p.y, p.z, object->facing);
				saved_divergence_warning= true;
			}
		}
		
		saved_point_count+= 1;
	}
#endif
}
static int Lua_Monster_Set_Active(lua_State *L)
{
    if (!lua_isboolean(L, 2))
        return luaL_error(L, "active: incorrect argument type");

    bool activate = lua_toboolean(L, 2);
    int monster_index = Lua_Monster::Index(L, 1);
    monster_data *monster = get_monster_data(monster_index);
    if (MONSTER_IS_PLAYER(monster))
        return luaL_error(L, "active: monster is a player");
    if (activate)
    {
        if (!MONSTER_IS_ACTIVE(monster))
            activate_monster(monster_index);
    }
    else
    {
        if (MONSTER_IS_ACTIVE(monster))
            deactivate_monster(monster_index);
    }
    return 0;
}
static int Lua_Monster_Get_Type(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    Lua_MonsterType::Push(L, monster->type);
    return 1;
}
示例#25
0
// ZZZ: if in predictive mode, restore the saved partial game-state (it'd better take us back
// to _exactly_ the same full game-state we saved earlier, else problems.)
static void
exit_predictive_mode()
{
	if(sPredictedTicks > 0)
	{
		for(short i = 0; i < dynamic_world->player_count; i++)
		{
			player_data* player = get_player_data(i);
			
			assert(player->monster_index == sSavedPlayerData[i].monster_index);

			{
				// We *don't* restore this tiny part of the game-state back because
				// otherwise the player can't use [] to scroll the inventory panel.
				// [] scrolling happens outside the normal input/update system, so that's
				// enough to persuade me that not restoring this won't OOS any more often
				// than []-scrolling did before prediction.  :)
				int16 saved_interface_flags = player->interface_flags;
				int16 saved_interface_decay = player->interface_decay;
				
				*player = sSavedPlayerData[i];

				player->interface_flags = saved_interface_flags;
				player->interface_decay = saved_interface_decay;
			}

			if(sSavedPlayerData[i].monster_index != NONE)
			{
				assert(get_monster_data(sSavedPlayerData[i].monster_index)->object_index == sSavedPlayerMonsterData[i].object_index);

				*get_monster_data(sSavedPlayerData[i].monster_index) = sSavedPlayerMonsterData[i];
				
				if(sSavedPlayerMonsterData[i].object_index != NONE)
				{
					assert(get_object_data(sSavedPlayerMonsterData[i].object_index)->parasitic_object == sSavedPlayerObjectData[i].parasitic_object);

					remove_object_from_polygon_object_list(sSavedPlayerMonsterData[i].object_index);
					
					*get_object_data(sSavedPlayerMonsterData[i].object_index) = sSavedPlayerObjectData[i];

					// We have to defer this insertion since the object lists could still have other players
					// in their predictive locations etc. - we need to reconstruct everything exactly as it
					// was when we entered predictive mode.
					deferred_add_object_to_polygon_object_list(sSavedPlayerMonsterData[i].object_index, sSavedPlayerObjectNextObject[i]);
					
					if(sSavedPlayerObjectData[i].parasitic_object != NONE)
						*get_object_data(sSavedPlayerObjectData[i].parasitic_object) = sSavedPlayerParasiticObjectData[i];
				}
			}
		}

		perform_deferred_polygon_object_list_manipulations();
		
		sPredictedTicks = 0;

		// Sanity checking
		if(sSavedTickCount != dynamic_world->tick_count)
			logWarning2("saved tick count %d != dynamic_world->tick_count %d", sSavedTickCount, dynamic_world->tick_count);

		if(sSavedRandomSeed != get_random_seed())
			logWarning2("saved random seed %d != get_random_seed() %d", sSavedRandomSeed, get_random_seed());
	}
}
示例#26
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);
							}
						}
					}
				}
			}
		}
static int Lua_Monster_Get_Action(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    Lua_MonsterAction::Push(L, monster->action);
    return 1;
}
static int Lua_Monster_Get_Mode(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    Lua_MonsterMode::Push(L, monster->mode);
    return 1;
}
static int Lua_Monster_Get_Vertical_Velocity(lua_State *L)
{
    monster_data* monster = get_monster_data(Lua_Monster::Index(L, 1));
    lua_pushnumber(L, (double) monster->vertical_velocity / WORLD_ONE);
    return 1;
}
static int Lua_Monster_Get_Vitality(lua_State *L)
{
    monster_data *monster = get_monster_data(Lua_Monster::Index(L, 1));
    lua_pushnumber(L, monster->vitality);
    return 1;
}