watchtower.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. class Watchtower extends BaseBuildingBase
  2. {
  3. typename ATTACHMENT_BARBED_WIRE = BarbedWire;
  4. typename ATTACHMENT_CAMONET = CamoNet;
  5. const float MAX_FLOOR_VERTICAL_DISTANCE = 0.5;
  6. const float MIN_ACTION_DETECTION_ANGLE_RAD = 0.35; //0.35 RAD = 20 DEG
  7. const float MAX_ACTION_DETECTION_DISTANCE = 2.0; //meters
  8. static const string BASE_VIEW_NAME = "level_";
  9. static const string BASE_WALL_NAME = "_wall_";
  10. static const string BASE_ROOF_NAME = "_roof";
  11. static const int MAX_WATCHTOWER_FLOORS = 3;
  12. static const int MAX_WATCHTOWER_WALLS = 3;
  13. void Watchtower()
  14. {
  15. }
  16. override string GetConstructionKitType()
  17. {
  18. return "WatchtowerKit";
  19. }
  20. override int GetMeleeTargetType()
  21. {
  22. return EMeleeTargetType.NONALIGNABLE;
  23. }
  24. /*override void EEHitBy(TotalDamageResult damageResult, int damageType, EntityAI source, int component, string dmgZone, string ammo, vector modelPos, float speedCoef)
  25. {
  26. super.EEHitBy(damageResult, damageType, source, component, dmgZone, ammo, modelPos, speedCoef);
  27. if (component == -1)
  28. {
  29. Print("EEHitBy: " + this + "; damageType: "+ damageType +"; source: "+ source +"; component: "+ component +"; dmgZone: "+ dmgZone +"; ammo: "+ ammo +"; modelPos: "+ modelPos);
  30. Print("GetDamage " + damageResult.GetDamage("","Health"));
  31. Print("GetHighestDamage " + damageResult.GetHighestDamage("Health"));
  32. }
  33. }*/
  34. //overriden for the express purpose of handling view geometry (interaction) animations
  35. override void UpdateVisuals()
  36. {
  37. super.UpdateVisuals();
  38. SetAnimationPhase( "level_1", 0); //always visible
  39. SetAnimationPhase( "level_1_wall_1", 0); //always visible
  40. SetAnimationPhase( "level_1_wall_2", 0); //always visible
  41. SetAnimationPhase( "level_1_wall_3", 0); //always visible
  42. string part_name = "";
  43. bool built = false;
  44. for ( int i = 1; i < MAX_WATCHTOWER_FLOORS; ++i )
  45. {
  46. //roof checks
  47. part_name = "" + BASE_VIEW_NAME + i + BASE_ROOF_NAME;
  48. built = GetConstruction().IsPartConstructed(part_name);
  49. //Print(part_name);
  50. //Print(built);
  51. //string tmp = "";
  52. if ( built )
  53. {
  54. SetAnimationPhase( BASE_VIEW_NAME + (i + 1), 0); //show
  55. for ( int j = 1; j < MAX_WATCHTOWER_WALLS + 1; ++j )
  56. {
  57. //tmp = BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j;
  58. //Print(tmp);
  59. SetAnimationPhase( BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j, 0); //show
  60. }
  61. }
  62. else
  63. {
  64. SetAnimationPhase( BASE_VIEW_NAME + (i + 1), 1 ); //hide
  65. for ( j = 1; j < MAX_WATCHTOWER_WALLS + 1; ++j )
  66. {
  67. //tmp = BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j;
  68. //Print(tmp);
  69. SetAnimationPhase( BASE_VIEW_NAME + (i + 1) + BASE_WALL_NAME + j, 1); //hide
  70. }
  71. }
  72. }
  73. }
  74. //--- ATTACHMENT & CONDITIONS
  75. override bool CanReceiveAttachment( EntityAI attachment, int slotId )
  76. {
  77. if ( !super.CanReceiveAttachment( attachment, slotId ) )
  78. return false;
  79. //because CanReceiveAttachment() method can be called on all clients in the vicinity, vertical distance check needs to be skipped on clients that don't
  80. //interact with the object through attach action (AT_ATTACH_TO_CONSTRUCTION)
  81. PlayerBase player;
  82. if ( !GetGame().IsDedicatedServer() )
  83. {
  84. //check action initiator (AT_ATTACH_TO_CONSTRUCTION)
  85. player = PlayerBase.Cast( GetGame().GetPlayer() );
  86. if ( player )
  87. {
  88. ConstructionActionData construction_action_data = player.GetConstructionActionData();
  89. PlayerBase action_initiator = construction_action_data.GetActionInitiator();
  90. if ( action_initiator == player )
  91. {
  92. construction_action_data.SetActionInitiator( NULL ); //reset action initiator
  93. }
  94. else
  95. {
  96. player = null; //do not do vertical check (next)
  97. }
  98. }
  99. }
  100. //
  101. return CheckSlotVerticalDistance( slotId, player );
  102. }
  103. //can put into hands
  104. override bool CanPutIntoHands( EntityAI parent )
  105. {
  106. return false;
  107. }
  108. override bool CanBeRepairedToPristine()
  109. {
  110. return true;
  111. }
  112. override bool PerformRoofCheckForBase( string partName, PlayerBase player, out bool result )
  113. {
  114. if (CfgGameplayHandler.GetDisablePerformRoofCheck())
  115. return false;
  116. if (partName != "level_1_base" && partName != "level_2_base" && partName != "level_3_base" && partName != "level_3_roof")
  117. {
  118. return false;
  119. }
  120. vector center;
  121. vector orientation = GetOrientation();
  122. vector edge_length;
  123. vector min_max[2];
  124. ref array<Object> excluded_objects = new array<Object>;
  125. ref array<Object> collided_objects = new array<Object>;
  126. excluded_objects.Insert( this );
  127. excluded_objects.Insert( player );
  128. if ( partName == "level_2_base" )
  129. {
  130. min_max[0] = GetMemoryPointPos( "level_2_wall_1_down_min" );
  131. min_max[1] = GetMemoryPointPos( "level_2_roof_max" );
  132. }
  133. else if ( partName == "level_3_base" )
  134. {
  135. min_max[0] = GetMemoryPointPos( "level_3_wall_1_down_min" );
  136. min_max[1] = GetMemoryPointPos( "level_3_wall_2_up_max" );
  137. }
  138. else if ( partName == "level_3_roof" )
  139. {
  140. min_max[0] = GetMemoryPointPos( "level_3_roof_min" );
  141. min_max[1] = GetMemoryPointPos( "level_3_roof_max" );
  142. }
  143. else
  144. {
  145. //min_max[0] = GetMemoryPointPos( "level_1_wall_1_up_min" );
  146. //min_max[0] = GetMemoryPointPos( "level_1_wall_1_down_min" );
  147. min_max[0] = GetMemoryPointPos( "level_1_collisioncheck_min" );
  148. min_max[1] = GetMemoryPointPos( "level_1_roof_max" );
  149. }
  150. center = GetPosition();
  151. center[1] = center[1] + ( min_max[1][1] + min_max[0][1] ) / 2;
  152. edge_length[0] = min_max[1][0] - min_max[0][0];
  153. edge_length[2] = min_max[1][2] - min_max[0][2];
  154. edge_length[1] = min_max[1][1] - min_max[0][1];
  155. result = false;
  156. /*result = */GetGame().IsBoxCollidingGeometry( center, orientation, edge_length, ObjIntersectView, ObjIntersectGeom, excluded_objects, collided_objects );
  157. if ( collided_objects.Count() > 0 )
  158. {
  159. foreach ( Object o : collided_objects )
  160. {
  161. if (Building.Cast(o))
  162. {
  163. result = true;
  164. }
  165. }
  166. }
  167. return true;
  168. }
  169. // --- INVENTORY
  170. override bool CanDisplayAttachmentSlot( int slot_id )
  171. {
  172. //super
  173. if ( !super.CanDisplayAttachmentSlot( slot_id ) )
  174. return false;
  175. string slot_name = InventorySlots.GetSlotName(slot_id);
  176. slot_name.ToLower();
  177. PlayerBase player = PlayerBase.Cast( GetGame().GetPlayer() );
  178. //base attachments
  179. if ( slot_name.Contains( "material_l1" ) || slot_name.Contains( "level_1_" ) )
  180. {
  181. if ( slot_name.Contains( "woodenlogs" ) )
  182. {
  183. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  184. }
  185. else
  186. {
  187. return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  188. }
  189. }
  190. else if ( slot_name.Contains( "material_l2" ) || slot_name.Contains( "level_2_" ) )
  191. {
  192. if ( slot_name.Contains( "woodenlogs" ) )
  193. {
  194. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  195. }
  196. else
  197. {
  198. return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  199. }
  200. }
  201. else if ( slot_name.Contains( "material_l3" ) || slot_name.Contains( "level_3_" ) )
  202. {
  203. if ( slot_name.Contains( "woodenlogs" ) )
  204. {
  205. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  206. }
  207. else
  208. {
  209. return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  210. }
  211. }
  212. return true;
  213. }
  214. override bool CanDisplayAttachmentCategory( string category_name )
  215. {
  216. //super
  217. if ( !super.CanDisplayAttachmentCategory( category_name ) )
  218. return false;
  219. //
  220. category_name.ToLower();
  221. PlayerBase player = PlayerBase.Cast( GetGame().GetPlayer() );
  222. //level 1
  223. if ( category_name.Contains( "level_1" ) )
  224. {
  225. if ( category_name.Contains( "level_1_" ) )
  226. {
  227. return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  228. }
  229. else
  230. {
  231. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  232. }
  233. }
  234. //level 2
  235. if ( category_name.Contains( "level_2" ) )
  236. {
  237. if ( category_name.Contains( "level_2_" ) )
  238. {
  239. return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  240. }
  241. else
  242. {
  243. return GetConstruction().IsPartConstructed( "level_1_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  244. }
  245. }
  246. //level 3
  247. if ( category_name.Contains( "level_3" ) )
  248. {
  249. if ( category_name.Contains( "level_3_" ) )
  250. {
  251. return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  252. }
  253. else
  254. {
  255. return GetConstruction().IsPartConstructed( "level_2_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  256. }
  257. }
  258. return true;
  259. }
  260. //returns true if attachment slot position is within given range
  261. override bool CheckSlotVerticalDistance( int slot_id, PlayerBase player )
  262. {
  263. string slot_name;
  264. InventorySlots.GetSelectionForSlotId( slot_id , slot_name );
  265. slot_name.ToLower();
  266. //wall attachments
  267. //level 1
  268. if ( slot_name.Contains( "material_l1" ) || slot_name.Contains( "level_1_" ) )
  269. {
  270. if ( slot_name.Contains( "woodenlogs" ) )
  271. {
  272. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  273. }
  274. else
  275. {
  276. return GetConstruction().IsPartConstructed( "level_1_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_1", player );
  277. }
  278. }
  279. //level 2
  280. if ( slot_name.Contains( "material_l2" ) || slot_name.Contains( "level_2_" ) )
  281. {
  282. if ( slot_name.Contains( "material_l2w" ) || slot_name.Contains( "level_2_wall" ) )
  283. {
  284. return GetConstruction().IsPartConstructed( "level_2_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  285. }
  286. else
  287. {
  288. if ( slot_name.Contains( "woodenlogs" ) )
  289. {
  290. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  291. }
  292. else
  293. {
  294. return GetConstruction().IsPartConstructed( "level_1_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_2", player );
  295. }
  296. }
  297. }
  298. //level 3
  299. if ( slot_name.Contains( "material_l3" ) || slot_name.Contains( "level_3_" ) )
  300. {
  301. if ( slot_name.Contains( "material_l3w" ) || slot_name.Contains( "level_3_wall" ) )
  302. {
  303. return GetConstruction().IsPartConstructed( "level_3_base" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  304. }
  305. else
  306. {
  307. if ( slot_name.Contains( "woodenlogs" ) )
  308. {
  309. return CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  310. }
  311. else
  312. {
  313. return GetConstruction().IsPartConstructed( "level_2_roof" ) && CheckMemoryPointVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, "level_3", player );
  314. }
  315. }
  316. }
  317. return true;
  318. }
  319. //returns true if player->mem_point position is within given range
  320. override bool CheckMemoryPointVerticalDistance( float max_dist, string selection, PlayerBase player )
  321. {
  322. if ( player )
  323. {
  324. //check vertical distance
  325. vector player_pos = player.GetPosition();
  326. vector pos;
  327. if ( MemoryPointExists( selection ) )
  328. {
  329. pos = ModelToWorld( GetMemoryPointPos( selection ) );
  330. }
  331. if ( Math.AbsFloat( player_pos[1] - pos[1] ) <= max_dist )
  332. {
  333. return true;
  334. }
  335. else
  336. {
  337. return false;
  338. }
  339. }
  340. return true;
  341. }
  342. override bool CheckLevelVerticalDistance( float max_dist, string selection, PlayerBase player )
  343. {
  344. if ( player )
  345. {
  346. if ( selection.Contains( "level_1_" ) )
  347. return CheckMemoryPointVerticalDistance( max_dist, "level_1", player );
  348. if ( selection.Contains( "level_2_" ) )
  349. return CheckMemoryPointVerticalDistance( max_dist, "level_2", player );
  350. if ( selection.Contains( "level_3_" ) )
  351. return CheckMemoryPointVerticalDistance( max_dist, "level_3", player );
  352. }
  353. return false;
  354. }
  355. // ---
  356. override void AfterStoreLoad()
  357. {
  358. super.AfterStoreLoad();
  359. UpdateVisuals();
  360. }
  361. override void OnPartBuiltServer( notnull Man player, string part_name, int action_id )
  362. {
  363. super.OnPartBuiltServer( player, part_name, action_id );
  364. //update visuals (server)
  365. UpdateVisuals();
  366. }
  367. override void OnPartDismantledServer( notnull Man player, string part_name, int action_id )
  368. {
  369. super.OnPartDismantledServer( player, part_name, action_id );
  370. //update visuals (server)
  371. UpdateVisuals();
  372. }
  373. override void OnPartDestroyedServer( Man player, string part_name, int action_id, bool destroyed_by_connected_part = false )
  374. {
  375. super.OnPartDestroyedServer( player, part_name, action_id );
  376. //update visuals (server)
  377. UpdateVisuals();
  378. }
  379. //--- ACTION CONDITIONS
  380. //returns dot product of player->construction direction based on existing/non-existing reference point
  381. override bool IsFacingPlayer( PlayerBase player, string selection )
  382. {
  383. vector ref_pos;
  384. vector ref_dir;
  385. vector player_dir;
  386. float dot;
  387. bool has_memory_point = MemoryPointExists( selection );
  388. if ( has_memory_point )
  389. {
  390. ref_pos = ModelToWorld( GetMemoryPointPos( selection ) );
  391. ref_dir = ref_pos - GetPosition();
  392. }
  393. else
  394. {
  395. ref_pos = GetPosition();
  396. ref_dir = ref_pos - player.GetPosition();
  397. }
  398. ref_dir.Normalize();
  399. ref_dir[1] = 0; //ignore height
  400. player_dir = player.GetDirection();
  401. player_dir.Normalize();
  402. player_dir[1] = 0; //ignore height
  403. if ( ref_dir.Length() != 0 )
  404. {
  405. dot = vector.Dot( player_dir, ref_dir );
  406. }
  407. if ( has_memory_point )
  408. {
  409. if ( dot < 0 && Math.AbsFloat( dot ) > MIN_ACTION_DETECTION_ANGLE_RAD )
  410. {
  411. return true;
  412. }
  413. }
  414. else
  415. {
  416. if ( dot > 0 && Math.AbsFloat( dot ) > MIN_ACTION_DETECTION_ANGLE_RAD )
  417. {
  418. return true;
  419. }
  420. }
  421. return false;
  422. }
  423. override bool IsFacingCamera( string selection )
  424. {
  425. vector ref_pos;
  426. vector ref_dir;
  427. vector cam_dir = GetGame().GetCurrentCameraDirection();
  428. if ( MemoryPointExists( selection ) )
  429. {
  430. ref_pos = ModelToWorld( GetMemoryPointPos( selection ) );
  431. ref_dir = ref_pos - GetPosition();
  432. ref_dir.Normalize();
  433. ref_dir[1] = 0; //ignore height
  434. cam_dir[1] = 0; //ignore height
  435. if ( ref_dir.Length() > 0.5 ) //if the distance (m) is too low, ignore this check
  436. {
  437. float dot = vector.Dot( cam_dir, ref_dir );
  438. if ( dot < 0 )
  439. {
  440. return true;
  441. }
  442. }
  443. }
  444. return false;
  445. }
  446. override bool IsPlayerInside( PlayerBase player, string selection )
  447. {
  448. if ( selection != "")
  449. {
  450. CheckLevelVerticalDistance( MAX_FLOOR_VERTICAL_DISTANCE, selection, player );
  451. }
  452. vector player_pos = player.GetPosition();
  453. vector tower_pos = GetPosition();
  454. vector ref_dir = GetDirection();
  455. ref_dir[1] = 0;
  456. ref_dir.Normalize();
  457. vector min,max;
  458. min = -GetMemoryPointPos( "interact_min" );
  459. max = -GetMemoryPointPos( "interact_max" );
  460. vector dir_to_tower = tower_pos - player_pos;
  461. dir_to_tower[1] = 0;
  462. float len = dir_to_tower.Length();
  463. dir_to_tower.Normalize();
  464. vector ref_dir_angle = ref_dir.VectorToAngles();
  465. vector dir_to_tower_angle = dir_to_tower.VectorToAngles();
  466. vector test_angles = dir_to_tower_angle - ref_dir_angle;
  467. vector test_position = test_angles.AnglesToVector() * len;
  468. if (test_position[0] > max[0] || test_position[0] < min[0] || test_position[2] > max[2] || test_position[2] < min[2] )
  469. {
  470. return false;
  471. }
  472. return true;
  473. }
  474. override bool HasProperDistance( string selection, PlayerBase player )
  475. {
  476. if ( MemoryPointExists( selection ) )
  477. {
  478. vector selection_pos = ModelToWorld( GetMemoryPointPos( selection ) );
  479. float distance = vector.Distance( selection_pos, player.GetPosition() );
  480. if ( distance >= MAX_ACTION_DETECTION_DISTANCE )
  481. {
  482. return false;
  483. }
  484. }
  485. return true;
  486. }
  487. override void SetActions()
  488. {
  489. super.SetActions();
  490. AddAction(ActionTogglePlaceObject);
  491. AddAction(ActionPlaceObject);
  492. AddAction(ActionFoldBaseBuildingObject);
  493. }
  494. //================================================================
  495. // DEBUG
  496. //================================================================
  497. //! Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare
  498. override array<string> OnDebugSpawnBuildExcludes()
  499. {
  500. array<string> excludes;
  501. #ifdef DIAG_DEVELOPER
  502. bool bWood = DiagMenu.GetBool(DiagMenuIDs.BASEBUILDING_WOOD);
  503. #else
  504. bool bWood = false;
  505. #endif
  506. if (bWood)
  507. {
  508. excludes = {"_metal_"};
  509. }
  510. else
  511. {
  512. excludes = {"_wood_"};
  513. }
  514. return excludes;
  515. }
  516. //Debug menu Spawn Ground Special
  517. override void OnDebugSpawn()
  518. {
  519. super.OnDebugSpawn();
  520. int i;
  521. for (i = 0; i < MAX_WATCHTOWER_FLOORS * MAX_WATCHTOWER_WALLS; ++i)
  522. {
  523. GetInventory().CreateInInventory("CamoNet");
  524. }
  525. for (i = 0; i < 2 * MAX_WATCHTOWER_WALLS; ++i)
  526. {
  527. BarbedWire wire = BarbedWire.Cast(GetInventory().CreateInInventory("BarbedWire"));
  528. wire.SetMountedState(true);
  529. }
  530. }
  531. }