class Watchtower extends BaseBuildingBase
{
typename ATTACHMENT_BARBED_WIRE = BarbedWire;
typename ATTACHMENT_CAMONET = CamoNet;
const float MAX_FLOOR_VERTICAL_DISTANCE = 0.5;
const float MIN_ACTION_DETECTION_ANGLE_RAD = 0.35; //0.35 RAD = 20 DEG
const float MAX_ACTION_DETECTION_DISTANCE = 2.0; //meters
static const string BASE_VIEW_NAME = "level_";
static const string BASE_WALL_NAME = "_wall_";
static const string BASE_ROOF_NAME = "_roof";
static const int MAX_WATCHTOWER_FLOORS = 3;
static const int MAX_WATCHTOWER_WALLS = 3;
void Watchtower()
{
}
override string GetConstructionKitType()
{
return "WatchtowerKit";
}
override int GetMeleeTargetType()
{
return EMeleeTargetType.NONALIGNABLE;
}
/*override void EEHitBy(TotalDamageResult damageResult, int damageType, EntityAI source, int component, string dmgZone, string ammo, vector modelPos, float speedCoef)
{
super.EEHitBy(damageResult, damageType, source, component, dmgZone, ammo, modelPos, speedCoef);
if (component == -1)
{
Print("EEHitBy: " + this + "; damageType: "+ damageType +"; source: "+ source +"; component: "+ component +"; dmgZone: "+ dmgZone +"; ammo: "+ ammo +"; modelPos: "+ modelPos);
Print("GetDamage " + damageResult.GetDamage("","Health"));
Print("GetHighestDamage " + damageResult.GetHighestDamage("Health"));
}
}*/
//overriden for the express purpose of handling view geometry (interaction) animations
override void UpdateVisuals()
{
super.UpdateVisuals();
SetAnimationPhase( "level_1", 0); //always visible
SetAnimationPhase( "level_1_wall_1", 0); //always visible
SetAnimationPhase( "level_1_wall_2", 0); //always visible
SetAnimationPhase( "level_1_wall_3", 0); //always visible
string part_name = "";
bool built = false;
for ( int i = 1; i < MAX_WATCHTOWER_FLOORS; ++i )
{
//roof checks
part_name = "" + BASE_VIEW_NAME + i + BASE_ROOF_NAME;
built = GetConstruction().IsPartConstructed(part_name);
//Print(part_name);
//Print(built);
//string tmp = "";
if ( built )
{
SetAnimationPhase( BASE_VIEW_NAME + (i + 1), 0); //show
for ( int j = 1; j < MAX_WATCHTOWER_WALLS + 1; ++j )
{
//tmp = BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j;
//Print(tmp);
SetAnimationPhase( BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j, 0); //show
}
}
else
{
SetAnimationPhase( BASE_VIEW_NAME + (i + 1), 1 ); //hide
for ( j = 1; j < MAX_WATCHTOWER_WALLS + 1; ++j )
{
//tmp = BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j;
//Print(tmp);
SetAnimationPhase( BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j, 1); //hide
}
}
}
}
//--- ATTACHMENT & CONDITIONS
override bool CanReceiveAttachment( EntityAI attachment, int slotId )
{
if ( !super.CanReceiveAttachment( attachment, slotId ) )
return false;
//because CanReceiveAttachment() method can be called on all clients in the vicinity, vertical distance check needs to be skipped on clients that don't
//interact with the object through attach action (AT_ATTACH_TO_CONSTRUCTION)
PlayerBase player;
if ( !g_Game.IsDedicatedServer() )
{
//check action initiator (AT_ATTACH_TO_CONSTRUCTION)
player = PlayerBase.Cast( g_Game.GetPlayer() );
if ( player )
{
ConstructionActionData construction_action_data = player.GetConstructionActionData();
PlayerBase action_initiator = construction_action_data.GetActionInitiator();
if ( action_initiator == player )
{
construction_action_data.SetActionInitiator( NULL ); //reset action initiator
}
else
{
player = null; //do not do vertical check (next)
}
}
}
//
return CheckSlotVerticalDistance( slotId, player );
}
//can put into hands
override bool CanPutIntoHands( EntityAI parent )
{
return false;
}
override bool CanBeRepairedToPristine()
{
return true;
}
override bool PerformRoofCheckForBase( string partName, PlayerBase player, out bool result )
{
if (CfgGameplayHandler.GetDisablePerformRoofCheck())
return false;
if (partName != "level_1_base" && partName != "level_2_base" && partName != "level_3_base" && partName != "level_3_roof")
{
return false;
}
vector center;
vector orientation = GetOrientation();
vector edge_length;
vector min_max[2];
ref array<Object> excluded_objects = new array<Object>;
ref array<Object> collided_objects = new array<Object>;
excluded_objects.Insert( this );
excluded_objects.Insert( player );
if ( partName == "level_2_base" )
{
min_max[0] = GetMemoryPointPos( "level_2_wall_1_down_min" );
min_max[1] = GetMemoryPointPos( "level_2_roof_max" );
}
else if ( partName == "level_3_base" )
{
min_max[0] = GetMemoryPointPos( "level_3_wall_1_down_min" );
min_max[1] = GetMemoryPointPos( "level_3_wall_2_up_max" );
}
else if ( partName == "level_3_roof" )
{
min_max[0] = GetMemoryPointPos( "level_3_roof_min" );
min_max[1] = GetMemoryPointPos( "level_3_roof_max" );
}
else
{
//min_max[0] = GetMemoryPointPos( "level_1_wall_1_up_min" );
//min_max[0] = GetMemoryPointPos( "level_1_wall_1_down_min" );
min_max[0] = GetMemoryPointPos( "level_1_collisioncheck_min" );
min_max[1] = GetMemoryPointPos( "level_1_roof_max" );
}
center = GetPosition();
center[1] = center[1] + ( min_max[1][1] + min_max[0][1] ) / 2;
edge_length[0] = min_max[1][0] - min_max[0][0];
edge_length[2] = min_max[1][2] - min_max[0][2];
edge_length[1] = min_max[1][1] - min_max[0][1];
result = false;
/*result = */g_Game.IsBoxCollidingGeometry( center, orientation, edge_length, ObjIntersectView, ObjIntersectGeom, excluded_objects, collided_objects );
if ( collided_objects.Count() > 0 )
{
foreach ( Object o : collided_objects )
{
if (Building.Cast(o))
{
result = true;
}
}
}
return true;
}
// --- INVENTORY
override bool CanDisplayAttachmentSlot( int slot_id )
{
//super
if ( !super.CanDisplayAttachmentSlot( slot_id ) )
return false;
string slot_name = InventorySlots.GetSlotName(slot_id);
slot_name.ToLower();
PlayerBase player = PlayerBase.Cast( g_Game.GetPlayer() );
//base attachments
if ( slot_name.Contains( "material_l1" ) || slot_name.Contains( "level_1_" ) )
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
}
else if ( slot_name.Contains( "material_l2" ) || slot_name.Contains( "level_2_" ) )
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
}
else if ( slot_name.Contains( "material_l3" ) || slot_name.Contains( "level_3_" ) )
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
}
return true;
}
override bool CanDisplayAttachmentCategory( string category_name )
{
//super
if ( !super.CanDisplayAttachmentCategory( category_name ) )
return false;
//
category_name.ToLower();
PlayerBase player = PlayerBase.Cast( g_Game.GetPlayer() );
//level 1
if ( category_name.Contains( "level_1" ) )
{
if ( category_name.Contains( "level_1_" ) )
{
return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
else
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
}
//level 2
if ( category_name.Contains( "level_2" ) )
{
if ( category_name.Contains( "level_2_" ) )
{
return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_1_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
}
//level 3
if ( category_name.Contains( "level_3" ) )
{
if ( category_name.Contains( "level_3_" ) )
{
return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_2_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
}
return true;
}
//returns true if attachment slot position is within given range
override bool CheckSlotVerticalDistance( int slot_id, PlayerBase player )
{
string slot_name;
InventorySlots.GetSelectionForSlotId( slot_id , slot_name );
slot_name.ToLower();
//wall attachments
//level 1
if ( slot_name.Contains( "material_l1" ) || slot_name.Contains( "level_1_" ) )
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
}
}
//level 2
if ( slot_name.Contains( "material_l2" ) || slot_name.Contains( "level_2_" ) )
{
if ( slot_name.Contains( "material_l2w" ) || slot_name.Contains( "level_2_wall" ) )
{
return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
else
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_1_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
}
}
}
//level 3
if ( slot_name.Contains( "material_l3" ) || slot_name.Contains( "level_3_" ) )
{
if ( slot_name.Contains( "material_l3w" ) || slot_name.Contains( "level_3_wall" ) )
{
return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
else
{
if ( slot_name.Contains( "woodenlogs" ) )
{
return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
else
{
return GetConstruction().IsPartConstructed( "level_2_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
}
}
}
return true;
}
//returns true if player->mem_point position is within given range
override bool CheckMemoryPointVerticalDistance( float max_dist, string selection, PlayerBase player )
{
if ( player )
{
//check vertical distance
vector player_pos = player.GetPosition();
vector pos;
if ( MemoryPointExists( selection ) )
{
pos = ModelToWorld( GetMemoryPointPos( selection ) );
}
if ( Math.AbsFloat( player_pos[1] - pos[1] ) <= max_dist )
{
return true;
}
else
{
return false;
}
}
return true;
}
override bool CheckLevelVerticalDistance( float max_dist, string selection, PlayerBase player )
{
if ( player )
{
if ( selection.Contains( "level_1_" ) )
return CheckMemoryPointVerticalDistance( max_dist, "level_1", player );
if ( selection.Contains( "level_2_" ) )
return CheckMemoryPointVerticalDistance( max_dist, "level_2", player );
if ( selection.Contains( "level_3_" ) )
return CheckMemoryPointVerticalDistance( max_dist, "level_3", player );
}
return false;
}
// ---
override void AfterStoreLoad()
{
super.AfterStoreLoad();
UpdateVisuals();
}
override void OnPartBuiltServer( notnull Man player, string part_name, int action_id )
{
super.OnPartBuiltServer( player, part_name, action_id );
//update visuals (server)
UpdateVisuals();
}
override void OnPartDismantledServer( notnull Man player, string part_name, int action_id )
{
super.OnPartDismantledServer( player, part_name, action_id );
//update visuals (server)
UpdateVisuals();
}
override void OnPartDestroyedServer( Man player, string part_name, int action_id, bool destroyed_by_connected_part = false )
{
super.OnPartDestroyedServer( player, part_name, action_id );
//update visuals (server)
UpdateVisuals();
}
//--- ACTION CONDITIONS
//returns dot product of player->construction direction based on existing/non-existing reference point
override bool IsFacingPlayer( PlayerBase player, string selection )
{
vector ref_pos;
vector ref_dir;
vector player_dir;
float dot;
bool has_memory_point = MemoryPointExists( selection );
if ( has_memory_point )
{
ref_pos = ModelToWorld( GetMemoryPointPos( selection ) );
ref_dir = ref_pos - GetPosition();
}
else
{
ref_pos = GetPosition();
ref_dir = ref_pos - player.GetPosition();
}
ref_dir.Normalize();
ref_dir[1] = 0; //ignore height
player_dir = player.GetDirection();
player_dir.Normalize();
player_dir[1] = 0; //ignore height
if ( ref_dir.Length() != 0 )
{
dot = vector.Dot( player_dir, ref_dir );
}
if ( has_memory_point )
{
if ( dot < 0 && Math.AbsFloat( dot ) > MIN_ACTION_DETECTION_ANGLE_RAD )
{
return true;
}
}
else
{
if ( dot > 0 && Math.AbsFloat( dot ) > MIN_ACTION_DETECTION_ANGLE_RAD )
{
return true;
}
}
return false;
}
override bool IsFacingCamera( string selection )
{
vector ref_pos;
vector ref_dir;
vector cam_dir = g_Game.GetCurrentCameraDirection();
if ( MemoryPointExists( selection ) )
{
ref_pos = ModelToWorld( GetMemoryPointPos( selection ) );
ref_dir = ref_pos - GetPosition();
ref_dir.Normalize();
ref_dir[1] = 0; //ignore height
cam_dir[1] = 0; //ignore height
if ( ref_dir.Length() > 0.5 ) //if the distance (m) is too low, ignore this check
{
float dot = vector.Dot( cam_dir, ref_dir );
if ( dot < 0 )
{
return true;
}
}
}
return false;
}
override bool IsPlayerInside( PlayerBase player, string selection )
{
if ( selection != "")
{
CheckLevelVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, selection, player );
}
vector player_pos = player.GetPosition();
vector tower_pos = GetPosition();
vector ref_dir = GetDirection();
ref_dir[1] = 0;
ref_dir.Normalize();
vector min,max;
min = -GetMemoryPointPos( "interact_min" );
max = -GetMemoryPointPos( "interact_max" );
vector dir_to_tower = tower_pos - player_pos;
dir_to_tower[1] = 0;
float len = dir_to_tower.Length();
dir_to_tower.Normalize();
vector ref_dir_angle = ref_dir.VectorToAngles();
vector dir_to_tower_angle = dir_to_tower.VectorToAngles();
vector test_angles = dir_to_tower_angle - ref_dir_angle;
vector test_position = test_angles.AnglesToVector() * len;
if (test_position[0] > max[0] || test_position[0] < min[0] || test_position[2] > max[2] || test_position[2] < min[2] )
{
return false;
}
return true;
}
override bool HasProperDistance( string selection, PlayerBase player )
{
if ( MemoryPointExists( selection ) )
{
vector selection_pos = ModelToWorld( GetMemoryPointPos( selection ) );
float distance = vector.Distance( selection_pos, player.GetPosition() );
if ( distance >= MAX_ACTION_DETECTION_DISTANCE )
{
return false;
}
}
return true;
}
override void SetActions()
{
super.SetActions();
AddAction(ActionTogglePlaceObject);
AddAction(ActionPlaceObject);
AddAction(ActionFoldBaseBuildingObject);
}
//================================================================
// DEBUG
//================================================================
//! Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare
override array<string> OnDebugSpawnBuildExcludes()
{
array<string> excludes;
#ifdef DIAG_DEVELOPER
bool bWood = DiagMenu.GetBool(DiagMenuIDs.BASEBUILDING_WOOD);
#else
bool bWood = false;
#endif
if (bWood)
{
excludes = {"_metal_"};
}
else
{
excludes = {"_wood_"};
}
return excludes;
}
//Debug menu Spawn Ground Special
override void OnDebugSpawn()
{
super.OnDebugSpawn();
int i;
for (i = 0; i < MAX_WATCHTOWER_FLOORS * MAX_WATCHTOWER_WALLS; ++i)
{
GetInventory().CreateInInventory("CamoNet");
}
for (i = 0; i < 2 * MAX_WATCHTOWER_WALLS; ++i)
{
BarbedWire wire = BarbedWire.Cast(GetInventory().CreateInInventory("BarbedWire"));
wire.SetMountedState(true);
}
}
}