tools.c 20 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. /**
  2. * \defgroup Tools
  3. * \desc Helpful functions & classes
  4. * @{
  5. */
  6. //--------------------------------------------------------------------------
  7. const int CALL_CATEGORY_SYSTEM = 0; // Runs always
  8. const int CALL_CATEGORY_GUI = 1; // Runs always (on client)
  9. const int CALL_CATEGORY_GAMEPLAY = 2; // Runs unless ingame menu is opened
  10. const int CALL_CATEGORY_COUNT = 3;
  11. // -------------------------------------------------------------------------
  12. class CallQueueContext
  13. {
  14. Class m_target;
  15. string m_function;
  16. ref Param m_params;
  17. bool m_valid;
  18. void CallQueueContext(Class target, string fn, Param params)
  19. {
  20. m_target = target;
  21. m_function = fn;
  22. m_params = params;
  23. m_valid = true;
  24. }
  25. void Call()
  26. {
  27. CallParams(m_params);
  28. }
  29. void CallParams(Param params)
  30. {
  31. if (params)
  32. {
  33. GetGame().GameScript.CallFunctionParams(m_target, m_function, NULL, params);
  34. }
  35. else
  36. {
  37. GetGame().GameScript.CallFunction(m_target, m_function, NULL, 0);
  38. }
  39. }
  40. void Invalidate() {
  41. m_valid = false;
  42. }
  43. bool IsValid(){
  44. return m_valid;
  45. }
  46. };
  47. //--------------------------------------------------------------------------
  48. /**
  49. \brief CallQueue Class provide "lazy" calls - when we don't want to execute function immediately but later during frame update (used mainly in UI)
  50. \n usage:
  51. @code
  52. GetGame().GetCallQueue(CALL_CATEGORY_GUI).Call(this, "Refresh"); // calls "Refresh" function on "this" with no arguments
  53. GetGame().GetCallQueue(CALL_CATEGORY_GUI).Call(this, "Show", new Param1<bool>(true)); // calls "Show" function on "this" with one bool argument
  54. GetGame().GetCallQueue(CALL_CATEGORY_GUI).Call(this, "SetPos", new Param2<float, float>(0.2, 0.5)); // calls "SetPos" function on "this" with two float arguments
  55. @endcode
  56. */
  57. class CallQueue extends array<ref CallQueueContext>
  58. {
  59. private bool m_processing;
  60. void CallQueue()
  61. {
  62. m_processing = false;
  63. }
  64. /**
  65. \brief System function, don't call it
  66. */
  67. void Tick()
  68. {
  69. if (m_processing) return;
  70. m_processing = true;
  71. while(Count() > 0)
  72. {
  73. CallQueueContext ctx = Get(0);
  74. if (!ctx.IsValid())
  75. {
  76. Remove(0);
  77. }
  78. else
  79. {
  80. Remove(0);
  81. ctx.Call();
  82. }
  83. }
  84. m_processing = false;
  85. }
  86. /**
  87. \brief Creates new call request, add it on queue and execute during frame update (depends on call category)
  88. @param obj target object on which function will be executed
  89. @param fn_name name of function (on object "obj") which will be executed
  90. @param params function arguments see Param for usage, default NULL (no arguments)
  91. \warning When object "obj" is deleted prior call execution, don't forget to remove calls for this object with RemoveCalls
  92. */
  93. void Call(Class obj, string fn_name, Param params = NULL)
  94. {
  95. Insert(new CallQueueContext(obj, fn_name, params));
  96. }
  97. /**
  98. \brief Removes all queued calls for object (call this function when object is going to be deleted)
  99. @param obj object for which you want remove all "lazy" calls
  100. */
  101. void RemoveCalls(Class obj)
  102. {
  103. if (Count())
  104. {
  105. for (int i = Count() - 1; i >= 0; i--)
  106. {
  107. CallQueueContext ctx = Get(i);
  108. if (ctx.m_target == obj)
  109. {
  110. ctx.Invalidate();
  111. }
  112. }
  113. }
  114. }
  115. };
  116. //--------------------------------------------------------------------------
  117. /**
  118. \brief DragQueue Class provide callbacks while mouse is dragging. Callback function must have exact arguments:
  119. @code
  120. void AnyFunctionName(int mouse_x, int mouse_y, bool is_dragging);
  121. @endcode
  122. When mouse button is released, callback function is called one more time with is_dragging = false. Then all callbacks are automatically removed from queue.
  123. \n usage:
  124. @code
  125. class XYZ
  126. {
  127. void UpdateDrag(int mouse_x, int mouse_y, bool is_dragging);
  128. }
  129. ...
  130. GetGame().GetDragQueue().Call(this, "UpdateDrag"); // calls "UpdateDrag" function on "this"
  131. @endcode
  132. */
  133. class DragQueue extends CallQueue
  134. {
  135. private ref Param3<int, int, bool> m_mouse_params; // x,y, is_holding_mb
  136. void DragQueue()
  137. {
  138. m_mouse_params = new Param3<int, int, bool>(0,0,true);
  139. }
  140. /**
  141. \brief System function, don't call it
  142. */
  143. override void Tick()
  144. {
  145. if (m_processing) return;
  146. m_processing = true;
  147. int last_index = 0;
  148. int mouse_x;
  149. int mouse_y;
  150. bool is_holding = false;
  151. CallQueueContext ctx;
  152. if (GetMouseState(MouseState.LEFT) & 0x80000000)
  153. {
  154. is_holding = true;
  155. }
  156. GetMousePos(mouse_x, mouse_y);
  157. if (!is_holding || mouse_x != m_mouse_params.param1 || mouse_y != m_mouse_params.param2)
  158. {
  159. m_mouse_params.param1 = mouse_x;
  160. m_mouse_params.param2 = mouse_y;
  161. m_mouse_params.param3 = is_holding;
  162. while (Count() > last_index)
  163. {
  164. ctx = Get(last_index);
  165. if (!ctx.IsValid())
  166. {
  167. Remove(last_index);
  168. }
  169. else
  170. {
  171. ctx.CallParams(m_mouse_params);
  172. last_index++;
  173. }
  174. }
  175. }
  176. // clear queue when mouse button is released
  177. if (!is_holding)
  178. {
  179. Clear();
  180. }
  181. m_processing = false;
  182. }
  183. }
  184. //--------------------------------------------------------------------------
  185. /**
  186. \brief TimerBase Class provide just interface for all Timer classes. Don't instance this class, use Timer class instead.
  187. */
  188. class TimerBase: Managed
  189. {
  190. protected bool m_running;
  191. protected bool m_loop;
  192. protected float m_duration;
  193. protected float m_time;
  194. protected array<TimerBase> m_timerQueue;
  195. protected float m_RunTime;
  196. void ~TimerBase()
  197. {
  198. if (!m_timerQueue) return;
  199. SetRunning(false);
  200. }
  201. /**
  202. \brief Pause Timer, internal counter is not restarted, so timer can continue later. Can be unpaused via Continue.
  203. */
  204. void Pause()
  205. {
  206. SetRunning(false);
  207. }
  208. /**
  209. \brief Timer continue when it was paused.
  210. */
  211. void Continue()
  212. {
  213. SetRunning(true);
  214. }
  215. /**
  216. \brief Stop Timer and restart internal counter. Cannot be unpaused, must be started again.
  217. */
  218. void Stop()
  219. {
  220. SetRunning(false);
  221. m_time = 0;
  222. }
  223. /**
  224. \return True when Timer is running (not stopped, not paused)
  225. */
  226. bool IsRunning()
  227. {
  228. return m_running;
  229. }
  230. /**
  231. \brief System function, don't call.
  232. */
  233. void Tick(float timeslice)
  234. {
  235. if (IsRunning())
  236. {
  237. m_RunTime += timeslice;
  238. m_time = m_time + timeslice;
  239. if (m_time >= m_duration)
  240. {
  241. if (m_loop)
  242. {
  243. m_time = m_time - m_duration;
  244. }
  245. else
  246. {
  247. SetRunning(false);
  248. m_time = 0;
  249. }
  250. OnTimer();
  251. }
  252. else
  253. {
  254. OnUpdate();
  255. }
  256. }
  257. }
  258. /**
  259. \brief System function, don't call.
  260. */
  261. void OnTimerQueueDestoryed()
  262. {
  263. m_timerQueue = NULL;
  264. }
  265. float GetTime()
  266. {
  267. return m_time;
  268. }
  269. float GetDuration()
  270. {
  271. return m_duration;
  272. }
  273. float GetRemaining()
  274. {
  275. return m_duration - m_time;
  276. }
  277. float GetRunTime()
  278. {
  279. return m_RunTime;
  280. }
  281. protected void OnInit(int category)
  282. {
  283. m_duration = 1;
  284. m_loop = false;
  285. m_time = 0;
  286. m_running = false;
  287. if (GetGame())
  288. m_timerQueue = GetGame().GetTimerQueue(category);
  289. else
  290. ErrorEx("Attempting to Init a timer when the game does not exist (GetGame() == null)");
  291. }
  292. protected void OnStart(float duration, bool loop)
  293. {
  294. m_RunTime = 0;
  295. m_duration = duration;
  296. m_loop = loop;
  297. m_time = 0;
  298. SetRunning(true);
  299. }
  300. protected void OnUpdate() {}
  301. protected void OnTimer() {}
  302. protected void SetRunning(bool running)
  303. {
  304. int index = -1;
  305. if (m_running == running) return;
  306. m_running = running;
  307. if (m_timerQueue == NULL) return;
  308. if (running)
  309. {
  310. if (m_timerQueue.Find(this) == -1)
  311. {
  312. m_timerQueue.Insert(this);
  313. }
  314. }
  315. else
  316. {
  317. index = m_timerQueue.Find(this);
  318. if (index != -1)
  319. {
  320. m_timerQueue.Remove(index);
  321. }
  322. }
  323. }
  324. };
  325. //--------------------------------------------------------------------------
  326. /**
  327. \brief TimerQueue Class using for system purpose only
  328. */
  329. class TimerQueue extends array<TimerBase>
  330. {
  331. private bool m_processing;
  332. // -------------------------------------------------------------------------
  333. void TimerQueue()
  334. {
  335. m_processing = false;
  336. }
  337. // -------------------------------------------------------------------------
  338. void ~TimerQueue()
  339. {
  340. if (Count())
  341. {
  342. for (int i = Count() - 1; i >= 0; i--)
  343. {
  344. Get(i).OnTimerQueueDestoryed();
  345. }
  346. Clear();
  347. }
  348. }
  349. // -------------------------------------------------------------------------
  350. void Tick(float timeslice)
  351. {
  352. if (m_processing) return;
  353. m_processing = true;
  354. if (Count())
  355. {
  356. for (int i = Count() - 1; i >= 0; i--)
  357. {
  358. Get(i).Tick(timeslice);
  359. }
  360. }
  361. m_processing = false;
  362. }
  363. };
  364. //--------------------------------------------------------------------------
  365. /**
  366. \brief Simple class for fading Widgets
  367. */
  368. class WidgetFadeTimer extends TimerBase
  369. {
  370. private Widget m_widget;
  371. bool m_fadeIn;
  372. float m_alpha;
  373. void WidgetFadeTimer()
  374. {
  375. OnInit(CALL_CATEGORY_GUI);
  376. m_fadeIn = true;
  377. }
  378. /**
  379. \brief Make "fade in" effect on Widget (transparency goes from 0.0 to 1.0)
  380. @param w widget which will be faded
  381. @param time duration of effect
  382. @param continue - if True continue from current alpha value, otherwise always begin from 0.0 alpha
  383. */
  384. void FadeIn(Widget w, float time, bool continue_ = false)
  385. {
  386. m_alpha = w.GetAlpha();
  387. if (continue_ && m_alpha > 0.95)
  388. {
  389. w.SetAlpha(1.0);
  390. w.Show(true);
  391. return;
  392. }
  393. m_widget = w;
  394. m_fadeIn = true;
  395. OnStart(time, false);
  396. if (m_widget)
  397. {
  398. m_alpha = m_widget.GetAlpha();
  399. m_widget.SetAlpha(0);
  400. m_widget.Show(true);
  401. }
  402. if (continue_)
  403. {
  404. m_time = m_alpha * time;
  405. }
  406. }
  407. /**
  408. \brief Make "fade out" effect on Widget (transparency goes from 1.0 to 0.0)
  409. @param w widget which will be faded
  410. @param time duration of effect
  411. @param continue - if True continue from current alpha value, otherwise always begin from 1.0 alpha
  412. */
  413. void FadeOut(Widget w, float time, bool continue_ = false)
  414. {
  415. m_alpha = w.GetAlpha();
  416. if (continue_ && m_alpha < 0.05)
  417. {
  418. w.SetAlpha(0);
  419. w.Show(false);
  420. return;
  421. }
  422. m_widget = w;
  423. m_fadeIn = false;
  424. OnStart(time, false);
  425. if (m_widget && !continue_)
  426. {
  427. m_alpha = 1.0;
  428. m_widget.SetAlpha(m_alpha);
  429. m_widget.Show(true);
  430. }
  431. if (continue_)
  432. {
  433. m_time = (1.0 - m_alpha) * time;
  434. }
  435. }
  436. override private void OnTimer()
  437. {
  438. if (m_widget)
  439. {
  440. if (m_fadeIn)
  441. {
  442. m_widget.SetAlpha(1);
  443. }
  444. else
  445. {
  446. m_widget.SetAlpha(0);
  447. m_widget.Show(false);
  448. }
  449. }
  450. }
  451. override private void OnUpdate()
  452. {
  453. float timeDiff = m_time / m_duration;
  454. float progress;
  455. if (m_widget)
  456. {
  457. if (m_fadeIn)
  458. {
  459. progress = timeDiff;
  460. }
  461. else
  462. {
  463. progress = Math.Lerp(m_alpha,0,timeDiff);
  464. progress = Math.Clamp(progress,0,1);
  465. }
  466. m_widget.SetAlpha(progress);
  467. }
  468. }
  469. };
  470. //--------------------------------------------------------------------------
  471. /**
  472. \brief Timer class. Use when you want call function after some time, or repeatedly in time intervals. Call is not executed after the Timer object is deleted.
  473. \n usage:
  474. @code
  475. class MyObject
  476. {
  477. ref Timer myTimer1;
  478. ref Timer myTimer2;
  479. ref Timer myTimer3;
  480. void MyObject()
  481. {
  482. myTimer1 = new Timer();
  483. myTimer1.Run(10, this, "Refresh"); // calls "Refresh" on "this" after 10 seconds
  484. myTimer2 = new Timer();
  485. myTimer2.Run(10, this, "Refresh", NULL, true); // calls "Refresh" on "this" every 10 seconds, until Pause or Stop is called
  486. myTimer3 = new Timer();
  487. myTimer3.Run(15, this, "Show", new Param1<bool>(false)); // calls "Show" on "this" with one bool argument after 15 seconds
  488. }
  489. void Refresh();
  490. void Show(bool visible);
  491. }
  492. @endcode
  493. */
  494. class Timer extends TimerBase
  495. {
  496. protected Managed m_target;
  497. protected string m_function;
  498. protected ref Param m_params;
  499. void Timer(int category = CALL_CATEGORY_SYSTEM)
  500. {
  501. OnInit(category);
  502. }
  503. /**
  504. \brief Starts timer.
  505. @param duration function is executed after this time (in seconds).
  506. @param obj target object on which function will be executed
  507. @param fn_name name of function (on object "obj") which will be executed
  508. @param params function arguments see Param for usage, default NULL (no arguments)
  509. @param loop when true, timer is looped endlessly and function is executed after every loop.
  510. */
  511. void Run(float duration, Managed obj, string fn_name, Param params = NULL, bool loop = false)
  512. {
  513. m_target = obj;
  514. m_function = fn_name;
  515. m_params = params;
  516. OnStart(duration, loop);
  517. }
  518. override protected void OnTimer()
  519. {
  520. if (m_params)
  521. {
  522. GetGame().GameScript.CallFunctionParams(m_target, m_function, NULL, m_params);
  523. m_params = NULL;
  524. }
  525. else
  526. {
  527. GetGame().GameScript.CallFunction(m_target, m_function, NULL, 0);
  528. }
  529. }
  530. override void Stop()
  531. {
  532. super.Stop();
  533. m_params = NULL;
  534. }
  535. };
  536. //--------------------------------------------------------------------------
  537. /**
  538. \brief AnimationTimer class. This timer is for animating float value.
  539. \n usage:
  540. @code
  541. class MyObject
  542. {
  543. ref AnimationTimer myAnim;
  544. void MyObject()
  545. {
  546. myAnim = new AnimationTimer();
  547. myAnim.Run(60, this, "Refresh");
  548. }
  549. void Refresh()
  550. {
  551. Print(myAnim.GetValue());
  552. }
  553. };
  554. @endcode
  555. */
  556. class AnimationTimer : TimerBase
  557. {
  558. private bool m_Active;
  559. private float m_TargetValue;
  560. private float m_TargetValueOriginal;
  561. private float m_Value;
  562. protected Managed m_TargetObject;
  563. protected string m_UpdateFunction;
  564. protected string m_FinishedFunction;
  565. protected ref Param m_Params;
  566. void AnimationTimer(int category = CALL_CATEGORY_SYSTEM)
  567. {
  568. OnInit(category);
  569. }
  570. void ~AnimationTimer()
  571. {
  572. SetRunning(false);
  573. }
  574. void Run(float targetVal, Managed obj, string updateFunc, string finishedFunc, float startingVal = 0, bool loop = false, float speed = 1.0, Param params = null, int category = CALL_CATEGORY_SYSTEM)
  575. {
  576. SetRunning(true);
  577. m_TargetObject = obj;
  578. m_UpdateFunction = updateFunc;
  579. m_FinishedFunction = finishedFunc;
  580. m_TargetValueOriginal = targetVal;
  581. m_TargetValue = targetVal;
  582. m_time = speed;
  583. m_loop = loop;
  584. m_Active = true;
  585. m_Params = params;
  586. m_Value = startingVal;
  587. }
  588. /**
  589. \brief Returns actual animated value.
  590. */
  591. float GetValue() {
  592. return m_Value;
  593. }
  594. override bool IsRunning()
  595. {
  596. return m_Active;
  597. }
  598. /**
  599. \brief Ticks the timer, is called by timer subsystem.
  600. */
  601. override void Tick(float timeslice)
  602. {
  603. if ( !m_Active )
  604. return;
  605. float diff = Math.AbsFloat(m_TargetValue - m_Value);
  606. float step = m_time * timeslice;
  607. if (diff < step)
  608. {
  609. m_Value = m_TargetValue;
  610. if (!m_loop)
  611. {
  612. m_Active = false;
  613. }
  614. else
  615. {
  616. if (m_TargetValue == m_TargetValueOriginal)
  617. {
  618. m_TargetValue = 0;
  619. }
  620. else
  621. {
  622. m_TargetValue = m_TargetValueOriginal;
  623. }
  624. }
  625. GetGame().GameScript.CallFunction(m_TargetObject, m_FinishedFunction, NULL, m_Params);
  626. }
  627. else
  628. {
  629. if (m_TargetValue > m_Value)
  630. {
  631. m_Value += step;
  632. }
  633. else
  634. {
  635. m_Value -= step;
  636. }
  637. }
  638. GetGame().GameScript.CallFunction(m_TargetObject, m_UpdateFunction, NULL, m_Params);
  639. }
  640. };
  641. class AnimatorTimer
  642. {
  643. private bool m_active = false;
  644. private bool m_loop = false;
  645. private float m_target_value = 0;
  646. private float m_value = 0;
  647. private float m_time = 0;
  648. /**
  649. \brief Starts animate value until value reaches target value.
  650. @param val \p float target value
  651. @param speed \p float speed of animating, units per second
  652. */
  653. void Animate(float val, float speed = 1.0)
  654. {
  655. m_target_value = val;
  656. m_loop = false;
  657. m_time = speed;
  658. m_active = true;
  659. }
  660. /**
  661. \brief Starts infinite animation loop <-1,1>. Based on sinus transition.
  662. @param speed \p float speed of animating , cycles per second (1.0 means one 360 degree sinus cycle per second: 0..1..0..-1..0)
  663. */
  664. void AnimateLoop(float speed = 1.0)
  665. {
  666. m_value = 0;
  667. m_target_value = 0;
  668. m_loop = true;
  669. m_time = speed;
  670. m_active = true;
  671. }
  672. /**
  673. \brief Returns actual animated value.
  674. */
  675. float GetValue() {
  676. return m_value;
  677. }
  678. /**
  679. \brief Returns target value. While AnimateLoop returns angle of cycle in radians.
  680. */
  681. float GetTargetValue() {
  682. return m_target_value;
  683. }
  684. /**
  685. \brief Sets both value and target value.
  686. */
  687. void SetValue(float val) {
  688. m_value = val;
  689. m_target_value = val;
  690. }
  691. bool IsRunning()
  692. {
  693. return m_active;
  694. }
  695. /**
  696. \brief Ticks the timer, is called by timer subsystem.
  697. */
  698. void Tick(float timeslice)
  699. {
  700. if ( !m_active ) return;
  701. if (m_loop)
  702. {
  703. m_target_value += m_time * Math.PI2 * timeslice;
  704. while (m_target_value > Math.PI2) m_target_value -= Math.PI2;
  705. m_value = Math.Sin(m_target_value);
  706. }
  707. else
  708. {
  709. float diff = Math.AbsFloat(m_target_value - m_value);
  710. float step = m_time * timeslice;
  711. if (diff < step)
  712. {
  713. m_value = m_target_value;
  714. m_active = false;
  715. }
  716. else
  717. {
  718. if (m_target_value > m_value)
  719. {
  720. m_value += step;
  721. }
  722. else
  723. {
  724. m_value -= step;
  725. }
  726. }
  727. }
  728. }
  729. };
  730. /**
  731. \brief Associative array template, with multiple values per key
  732. \n usage:
  733. @code
  734. ref multiMap<string, int> myMap = new multiMap<string, int>;
  735. myMap.Insert("jozo", 7);
  736. myMap.Insert("jozo", 3);
  737. myMap.Insert("jozo", 5);
  738. myMap.Insert("fero", 98);
  739. myMap.Insert("fero", 35);
  740. myMap.Insert("fero", 17);
  741. Print("Jozo:");
  742. myMap.Get("jozo").Debug();
  743. Print("Fero:");
  744. myMap.Get("fero").Debug();
  745. @endcode
  746. */
  747. class multiMap<Class K, Class V>
  748. {
  749. private ref array<ref array<V> > m_values;
  750. private ref array<K> m_keys;
  751. bool HasKey(K key)
  752. {
  753. int index = -1;
  754. if (m_keys)
  755. {
  756. index = m_keys.Find(key);
  757. }
  758. if (index != -1)
  759. {
  760. return true;
  761. }
  762. return false;
  763. }
  764. array<V> Get(K key)
  765. {
  766. int index = -1;
  767. if (m_keys)
  768. {
  769. index = m_keys.Find(key);
  770. }
  771. if (index != -1)
  772. {
  773. return m_values.Get(index);
  774. }
  775. return NULL;
  776. }
  777. array<V> GetByIndex(int index)
  778. {
  779. return m_values.Get(index);
  780. }
  781. K GetKeyByIndex(int index)
  782. {
  783. return m_keys.Get(index);
  784. }
  785. void Insert(K key, V value)
  786. {
  787. int index = -1;
  788. if (!m_keys)
  789. {
  790. m_keys = new array<K>;
  791. m_values = new array<ref array<V> >;
  792. }
  793. else
  794. {
  795. index = m_keys.Find(key);
  796. }
  797. if (index == -1)
  798. {
  799. array<V> value_array = new array<V>;
  800. value_array.Insert(value);
  801. m_keys.Insert(key);
  802. m_values.Insert(value_array);
  803. }
  804. else
  805. {
  806. m_values.Get(index).Insert(value);
  807. }
  808. }
  809. void RemoveByIndex(int index)
  810. {
  811. m_keys.Remove(index);
  812. m_values.Remove(index);
  813. }
  814. void Remove(K key)
  815. {
  816. int index = -1;
  817. if (m_keys)
  818. {
  819. index = m_keys.Find(key);
  820. }
  821. if (index != -1)
  822. {
  823. RemoveByIndex(index);
  824. }
  825. }
  826. int Count()
  827. {
  828. if (m_keys)
  829. {
  830. return m_keys.Count();
  831. }
  832. return 0;
  833. }
  834. void Clear()
  835. {
  836. if ( m_keys && m_values)
  837. {
  838. m_keys.Clear();
  839. m_values.Clear();
  840. }
  841. }
  842. void ~multiMap()
  843. {
  844. Clear();
  845. }
  846. };
  847. // at last one template definition should be here, for template initialization in this script module
  848. typedef map<string, string> TStringMap;
  849. int GetTemperatureColor(int temperature)
  850. {
  851. int alpha = 255;
  852. int red = 153;
  853. int green = 153;
  854. int blue = 153;
  855. if (temperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
  856. {
  857. temperature = temperature - 20;
  858. temperature = Math.Clamp( temperature, -50, 50);
  859. temperature = Math.AbsInt(temperature);
  860. red = Math.Clamp ( red - ((red/50 )*temperature), 0, 255);
  861. green = Math.Clamp ( green - ((green/50 )*temperature), 0, 255);
  862. blue = Math.Clamp ( blue+((blue/50)*temperature), 0, 255);
  863. }
  864. else if (temperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  865. {
  866. temperature = Math.Clamp(temperature, -100, 100);
  867. blue = Math.Clamp (blue - ((blue / 100) * temperature), 0, 255);
  868. green = Math.Clamp (green - ((green / 100) * temperature), 0, 255);
  869. red = Math.Clamp (red + ((red / 100) * temperature), 0, 255);
  870. }
  871. return ARGB(alpha, red, green, blue);
  872. }
  873. //! Return value from profile variable, if variable with given name is not present, default value is returned
  874. bool GetProfileValueBool(string name, bool def = false)
  875. {
  876. string value;
  877. if (GetGame().GetProfileString(name, value))
  878. {
  879. if (value == "true" || value == "1")
  880. {
  881. return true;
  882. }
  883. else
  884. {
  885. return false;
  886. }
  887. }
  888. else
  889. {
  890. return def;
  891. }
  892. }
  893. //! Writes bool variable to profile, after write don't forget to call CGame::SaveProfile() to save profile to storage!
  894. void SetProfileValueBool(string name, bool value)
  895. {
  896. if (value)
  897. {
  898. GetGame().SetProfileString(name, "1");
  899. }
  900. else
  901. {
  902. GetGame().SetProfileString(name, "0");
  903. }
  904. }
  905. int GetNumberOfSetBits(int i)//leaving here for legacy/modding reasons
  906. {
  907. return Math.GetNumberOfSetBits(i);
  908. }
  909. /** @}*/