From 08daf9c7d838e0d99e98b23ba993ed3b72af816a Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 14:49:33 -0400 Subject: [PATCH 1/7] Restore state after accidental merge --- engine/openbor.c | 47214 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42831 insertions(+), 4383 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 3e12ae272..683b2852f 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -13773,4386 +13773,42834 @@ void lcmHandleCommandType(ArgList *arglist, s_model *newchar, char *filename) newchar->type = TYPE_NPC; newchar->bounce = 1; newchar->move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_MAX_Z | MOVE_CONFIG_SUBJECT_TO_MIN_Z | MOVE_CONFIG_SUBJECT_TO_OBSTACLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL); - newchar->move_config_flags &= ~MOVE_CONFIG_NO_A×n÷çÏÊ׬¢h­µçHB‚ˆYŠÙ[‹O˜Ý\ÝÛWÝ\™Ù]OH•S\Ù[‹O˜Ý\ÝÛWÝ\™Ù] O™^\ÝÈ -H\™Ù]H›Ü›X[Ùš[™Ý\™Ù] - LK -Nˆ[ÙH\™Ù]HÙ[‹O˜Ý\ÝÛWÝ\™Ù]‚ˆYŠ\™Ù] ‰ˆJ˜XÝ[Û—ØÚXÚרØ[—Ù[XYÙJÙ[‹\™Ù] [™\™XÝ -JJBˆˆ™]\›ˆ ˆB‚ˆYˆ - -Ù[‹O™XÚÚ[™È ˆPÒ×ÐPÕU‘JH ‰ˆÙ[‹O˜[š[[�[HOHS’WÑPÒÊBˆˆYˆ -]\™Ù][˜Z\Š\™Ù] -HJ\™Ù] O™XÚÚ[™È ˆPÒ×ÐPÕU‘JHˆ -XÚXÚ×ܘ[™ÙWÝ\™Ù]Ø[ -Ù[‹\™Ù] S’WÑPÒË  -H ‰‚ˆY™ŠÙ[‹OœÜÚ][Û‹ž \™Ù] OœÜÚ][Û‹ž -Hˆܘ[™Ù^ ‰ˆY™ŠÙ[‹OœÜÚ][Û‹ž‹\™Ù] OœÜÚ][Û‹žŠHˆܘ[™Ù^ŠH -BˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆžYXÚÜš\ÙJÙ[ŠNˆ™]\›ˆ NˆBˆB‚ˆYŠ]\™Ù]J\™Ù] O™XÚÚ[™È ˆPÒ×ÐPÕU‘JJBˆˆ™]\›ˆ ˆBˆ[ÙBˆˆYˆ -Ù[‹O™XÚÚ[™È ˆPÒ×ÐPÕU‘JBˆˆ™]\›ˆ NˆBˆ[ÙBˆˆ[�˜[™ÙWÙ›YÈHÚXÚ×ܘ[™ÙWÝ\™Ù]Ø[ -Ù[‹\™Ù] S’WÑPÒË  -NˆYˆ -\˜[™ÙWÙ›YÊBˆˆYˆ -Y™ŠÙ[‹OœÜÚ][Û‹ž \™Ù] OœÜÚ][Û‹ž -HHܘ[™Ù^ ‰‚ˆY™ŠÙ[‹OœÜÚ][Û‹ž‹\™Ù] OœÜÚ][Û‹žŠHHܘ[™Ù^ŠH˜[™ÙWÙ›YÈH NˆBˆYˆ -\˜[™ÙWÙ›YÊH™]\›ˆ ‚ˆYŠÙ[‹O™XÚÚ[™ÈOHPÒ×Ó“Ó‘JBˆˆžYXÚÊÙ[ŠNˆBˆ™]\›ˆ NˆBˆB‚ˆ™]\›ˆ ŸB‚‚‹ËÈK’H›ÛÝ�›ÚYÛÛ[[Û—Ý[šÊ -BžÂ‚ˆYŠÙ[‹O™X]ÜÝ]H ˆPUÔÕUWÑPQ -Bˆˆ™]\›ŽÂˆH‚ˆ ËÚYŠÚXÚÜ[›™Y - -JH™]\›ŽÂ‚ˆ ËÈÛȘ\ˆ]Ø^H ÈHØ\œˆYŠZWØÚXÚ×Ü™XØ[ - -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈš\ÙOÈžHš\ÙH]XÚˆYŠZWØÚXÚ×ÛYJ -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈ\ØØ\OˆYŠZWØÚXÚ×ÙܘX˜™Y - -JBˆˆ™]\›ŽÂˆB‚ˆ ËÙܘX˜š[™ÈÛÛY][™ÂˆYŠZWØÚXÚ×ÙܘXŠ -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈ[™[ZY\ÈØ[ˆ›ÝÈ\ØØ\H›Û‹ZÛ›ØÚÙÝÛˆÜ[[XYÙH -Ú]HÙZ\™˜\ÙJHBˆYŠZWØÚXÚ×Ù\ØØ\J -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈ�\ÞHšYÚ›ÝψYŠZWØÚXÚר�\ÞJ -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈYKÛÈžHÈ]XÚÈ܈�YÙH™^[Ý™Bˆ ËÈÛ�[Ý™HYˆ˜[[�ÈHÛH܈Ù™ˆHØ[ˆYŠÛÛ[[Û—Ø]XÚÊ -JBˆˆ™]\›ŽÂˆB‚ˆ ËÈ\™Ù]\ÈXÚÚ[™ÏÈžHÈXÚÚ[™Ë‹‚ˆYŠZWØÚXÚ×ÙXÚÚ[™Ê -JBˆˆ™]\›ŽÂˆB‚ˆÛÛ[[Û—Û[Ý™J -NŸB‚‹ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË‚�›ÚYÝZXÚYJ -BžÂˆˆYŠÝ[YHÙ[‹OœÝ[[YJBˆˆ™]\›ŽÂˆBˆ]™[ØÛÛ\]YHÙ[‹O˜›ÜÜΈ]™[ØÛÛ\]YÙY™X][™×Ø›ÜÜÈHÙ[‹O˜›ÜÜ΂ˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÔÕRPÒQJNŸB‚‚‚‹ËÈ™KY[�\ˆ^YšY[‹ËÈ\ÙYžH^Y\—Ù˜[[™^Y\—ÝZÙY[XYÙB�›ÚY^Y\—ÙYWÙ[�]J[�]JˆXÝ[™×Ù[�]JBžÂˆ[�^Y\š[™^HXÝ[™×Ù[�]KOœ^Y\š[™^ˆ[�HH ‚ˆYŠJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÓU‘T×ÐPÕU‘JJBˆˆ K\^Y\–Ü^Y\š[™^K›]™\ΈB‚ˆYŠš\œÝ^Y\ˆOHXÝ[™×Ù[�]JBˆˆš\œÝ^Y\ˆH•SˆB‚ˆ^XÝ]WÜYWÜØÜš\ -^Y\š[™^ -N‚ˆYŠ›ÛX^�\Ú™\Ù]ÍH�H JBˆˆ›ÛX^�\Ú™\Ù]Ü^Y\š[™^HH^Y\–Ü^Y\š[™^K™[� Oœ�\Ú ›X^ˆBˆ^Y\–Ü^Y\š[™^K™[�H•Sˆ^Y\–Ü^Y\š[™^KœÜ]ÛšX[HXÝ[™×Ù[�]KO›[Ù[]KšX[ˆ^Y\–Ü^Y\š[™^KœÜ]Û›\HXÝ[™×Ù[�]KO›[Ù[]K›\‚ˆ ʈˆ -ˆ[™HH›ÙKˆYˆ[žHÛÜœÙH›Y܈ -ˆ\™HÙ] ÙHX]™H[�]HÛˆØÜ™Y[ˆˆ -ˆ[™XZÙH][™\� ˆÝ\�Ú\ÙHÙHØ[‚ˆ -ˆ�\Ý�[ˆÚ[�[˜Ý[ۈș[[Ý™K‚ˆ -ˆˆ -ˆ‘SSÕ‘WÐÓÔ”ÑWʈ›YÜÈ™Y™\ˆÈÝȈ -ˆH]™H[�]H\È™[[Ý™Yœ›ÛHHˆ -ˆØ[YHÚ[HX]š[™ÈHÛÜœÙH™Z[™ ‚ˆ -‹Â‚ˆÝ]XÈÛÛœÝWÙX]ØÛÛ™šY×Ù›YÜÈX]™WØÛÜœÙHHPUÐÓÓ‘’Q×Ô‘SSÕ‘WÐÓÔ”ÑWÐRTˆPUÐÓÓ‘’Q×Ô‘SSÕ‘WÐÓÔ”ÑWÑÔ“ÕS‘‚ˆYˆ -XÝ[™×Ù[�]KO›[Ù[]K™X]ØÛÛ™šY×Ù›YÜÈ ˆX]™WØÛÜœÙJHˆXÝ[™×Ù[�]KO�[šÈH•SˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆH•SˆXÝ[™×Ù[�]KO™X]ÜÝ]HHPUÔÕUWÐÓÔ”ÑNˆH[ÙHˆÚ[Ù[�]JXÝ[™×Ù[�]KÒSÑS•UWÕ’QÑÑT—ÔVQT—ÑPU -NˆB‚ˆYŠ^Y\–Ü^Y\š[™^K›]™\ÈH -Bˆˆ[�[ÜÚÛÈH ‚‚BKËÈÛÝ[��[X™\ˆÙˆÓÉÙ -XY -H^Y\œËžHÛÜ[™È^Y\‚‚BKËÈ[™^\È[™[˜Ü™[Y[�[™ÈÚ[ˆ^Y\ˆÙ\È›Ý]™B‚BKËÈ[ˆ[�]K‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆ‚BBZYˆ -\^Y\–ÚWK™[� -B‚BB^‚BBBJÊØ[ÜÚÛ΂BB_BˆBˆ‚BKËÈYˆ[^Y\œÈ\™HÓÉÙ [ˆÓÈÛÝ[�H K‚‚BKËÈÝ\�Ú\ÙKÙ]]È ‚‚BX[ÜÚÛÈH -[ÜÚÛÈ�HPVÔVQT”ÊHÈ Hˆ ‚‚BKËÈ[^Y\œÈÓÉÙˆYŠ[ÜÚÛÊBˆ‚BBZ[�[ÜÛ›Ú›Ú[ˆH ‚BBZ[�[ÜÛ›ØÜ™Y]ÈH ‚‚BBKËÈ[^Y\œÈ“Õ›Ú[š[™Ï‚BBKËÈØ[YHÙÚXÈ\È[^Y\ˆÓˈˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆ‚BBBZYˆ -\^Y\–ÚWKš›Ú[š[™ÊB‚BBB^‚BBBBJÊØ[ÜÛ›Ú›Ú[ŽÂ‚BBB_BˆB‚ˆ[ÜÛ›Ú›Ú[ˆH -[ÜÛ›Ú›Ú[ˆ�HPVÔVQT”ÊHÈ Hˆ ‚‚BBKËÈ[^Y\œÈÝ]وܙY]Ï‚BBKËÈØ[YHÙÚXÈ\È[^Y\ˆÓË‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆ‚BBBZYˆ -^Y\–ÚWK˜Ü™Y]È JB‚BBB^‚BBBBJÊØ[ÜÛ›ØÜ™Y]΂BBB_BˆBBBB‚ˆ[ÜÛ›ØÜ™Y]ÈH -[ÜÛ›ØÜ™Y]È�HPVÔVQT”ÊHÈ Hˆ ‚‚BBKËÈÙ]H[Y\ˆÈH LÙXÛÛ™ÛÝ[�ÝÛ‹‚ˆ[Y[Y�H L -ˆÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYY‚‚BBKËÈ›ÈÛ™H›Ú[š[™È[�ˆYŠ[ÜÛ›Ú›Ú[ŠBˆ‚BBBKËÈYˆH^Y\ˆØ[‰ÝÛÛ�[�YK] ÜÈÙ]H[YHÝ™\‚‚BBBKËÈÈ[™[[ÜÝ[œÝ[�HÛÈ^HÛÛ‰Ý]™HÈØZ] ‚‚‚BBBKËÈYˆ›ÜÚ\™H\È[˜X›Y Ü™Y]Ú\™\È\™H›Ý[ÝÙY ˆ™\šYžH[‚BBBKËÈ^Y\ˆ[™]šYX[Ü™Y]Ý\Y\È\™H[\KˆÝ\�Ú\ÙHÜ™Y]‚BBBKËÈÚ\™\È\™H[ÝÙY ÛÈ™\šYžHÛÛوܙY]È\È[\K‚‚BBBZYˆ -›ÜÚ\™JB‚BBB^‚BBBBZYˆ -[ÜÛ›ØÜ™Y]ÊB‚BBBB^‚BBBBB][Y[Y�HÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYY È ŽÂ‚BBBB_BBBBBB‚BBB_B‚BBBY[ÙB‚BBB^‚BBBBZYˆ -Ü™Y]È JB‚BBBB^‚BBBBB][Y[Y�HÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYY È ŽÂ‚BBBB_B‚BBB_BˆBˆB‚ˆYŠXÝ[™×Ù[�]KO›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜרÛÛ™][Ûˆ ˆÑPTÓ—ÓÔÔ×ÐÓÓ‘USÓ—ÔÕQÑJBˆˆ^Y\–Ü^Y\š[™^K�ÙX\�[HH]™[ OœÙ]ÙX\ˆB‚ˆYŠ›ÛX^�\Ú™\Ù]ÍHOH ŠBˆˆ›ÛX^�\Ú™\Ù]Ü^Y\š[™^HH ˆBˆ‚B\™]\›ŽÂˆBˆ[ÙBˆˆÜ]Ûœ^Y\Š^Y\š[™^ -Nˆ^XÝ]WÜ™\Ü]Û—ÜØÜš\ -^Y\š[™^ -NˆYŠ[›Ù›Ü[ŠBˆˆYˆ -Ø]™Y]Kš›Þ\�[X›VÜ^Y\š[™^JHÛÛ�›ÛÜ�[X›J^Y\š[™^  K L�JNˆ›ÜØ[Ù[™[ZY\Ê -NˆBˆB‚ˆYŠ[]™[ O››Ü™\Ù] -Bˆˆ[Y[Y�H]™[ OœÙ][YH -ˆÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYYÈ ËÈ™Xˆ �  Œ H H\È[™H[Ý™Y\™HÈÙ]Ý\ÝÛH[YBˆB‚ŸB‚�›ÚY^Y\—ÙYJ›ÚY -BžÂˆ^Y\—ÙYWÙ[�]JÙ[ŠNŸB‚‚‚š[�^Y\—Ýž[[Ý™J›Ø]\‹›Ø]™\ŠBžÂˆ™]\›ˆÛÛ[[Û—Ýž[[Ý™J\‹™\ŠNŸB‚š[�ÚXÚ×Ù[™\™ÞJWØÛÜÝØÚXÚÈÚXÚ [�[šJBžÂˆ[�™\Ý[H�SÑNˆWÙ[�]WÝ\H\NÈ ËÑ[�]H\K‚ˆ×Ù[™\™ÞWØÛÜÝ[™\™ÞWØÛÜÝ‚Hˆ ËÈÙ][š[X][Û‰ÜÈ[™\™ÞWØÛÜÝ]K‚ˆ[™\™ÞWØÛÜÝ ˜ÛÜÝHÙ[‹O›[Ù[]K˜[š[X][Û–Ø[šWKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ‚Y[™\™ÞWØÛÜÝ ™\ØX›HHÙ[‹O›[Ù[]K˜[š[X][Û–Ø[šWKO™[™\™ÞWØÛÜÝ ™\ØX›N‚Y[™\™ÞWØÛÜÝ ›\Û›HHÙ[‹O›[Ù[]K˜[š[X][Û–Ø[šWKO™[™\™ÞWØÛÜÝ ›\Û›N‚ˆ ËÈÙ][�]H\K‚ˆ\BHHÙ[‹O›[Ù[]K�\N‚ˆ ËÈYˆÙIÜ™Hš[™[™ÜXÚX[\ÈÝ™\œšY[‹[‚ˆ ËÈ™]\›ˆ˜[ÙK‚ˆYŠ\H ˆ -TWÑS‘SVHTWÓ”ÊJBˆˆYŠÚXÚרš[™ÛÝ™\œšYJÙ[‹’S‘ÐÓÓ‘’Q×ÓÕ‘T”’QWÔÔPÒPSÐRJJBˆˆ™]\›ˆ�SÑNˆBˆBˆ[ÙHYŠ\H ˆTWÔVQTŠBˆˆYŠÚXÚרš[™ÛÝ™\œšYJÙ[‹’S‘ÐÓÓ‘’Q×ÓÕ‘T”’QWÔÔPÒPSÔVQTŠJBˆˆ™]\›ˆ�SÑNˆBˆB‚ˆYŠ˜[Y[š[JÙ[‹[šJJBˆˆ ËÈØ\ÚÙ^K[[Ûˆ‹‚ˆ ËÈ Œ L L KLˆ ˈ ËÈ]\È›ÝÈÜÜÚX›HÈ[™]šYX[H\ØX›HÜXÚX[ˈ[‚ˆ ËÈX[žHØ\Ù\È -ÙX\ÛœÈ[ˆ\�XÝ[\ŠH\ÈØ[‚Z[Ý]ÝÛˆH™YY›Ü‚ˆ ËÈÝ\\™›[Ý\È[Ù[ÈÚ[ˆY™™\š[™ÈXš[]Y\È\™H\Ú\™Y›Üˆ^Y\œËˆ ËÈ[™[ZY\Ë܈œÜË‚ˆ ËÈܘ]\È - L LŒ ŒJHš^YH™]Èœ›ÚÙ[ˆÛÙH›Üˆ™\ØX›Hˆ›YÈÚXÚ˘XÚÈÈH™]š[Ý\ÈÛÙBˆYŠJ[™\™ÞWØÛÜÝ ™\ØX›HOH\BBBBBBBBBBBBBKËÈ\ØX›YžH\Oˆ -[™\™ÞWØÛÜÝ ™\ØX›HOH LJBBBBBBBBBBBH ËÈ\ØX›Y›Üˆ[ˆ -[™\™ÞWØÛÜÝ ™\ØX›HOH Lˆ ‰ˆ -\H ˆ -TWÑS‘SVHTWÓ”ÊJJBBH ËÈ\ØX›Y›Üˆ[ROˆ -[™\™ÞWØÛÜÝ ™\ØX›HOH LÈ ‰ˆ -\H ˆ -TWÔVQTˆTWÓ”ÊJJBH ËÈ\ØX›Y›Üˆ^Y\œÈ ˆ”Üψ -[™\™ÞWØÛÜÝ ™\ØX›HOH M ‰ˆ -\H ˆ -TWÔVQTˆTWÑS‘SVJJJJJH ËÈ\ØX›Y›Üˆ[ROˆˆ ËÈ›ÈÙX[܈ÙX[\È\ÜËÜØ[YH\È[™\™ÞHÛÜ݈Yˆ -\Ù[‹OœÙX[Ù[‹OœÙX[�H[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠ -ÚXÚOHS‘T‘ÖWÕTWÓT ‰ˆ -[™\™ÞWØÛÜÝ ›\Û›HOHÓÔÕÕTWÒÓÓ“JH ‰ˆ -Ù[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[��H[™\™ÞWØÛÜÝ ˜ÛÜÝ -JH‚BBBB_ -ÚXÚOHS‘T‘ÖWÕTWÒ ‰ˆ -[™\™ÞWØÛÜÝ ›\Û›HOHÓÔÕÕTWÓTÓÓ“JH ‰ˆ -Ù[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ[™\™ÞWØÛÜÝ ˜ÛÜÝ -JJBˆˆ™\Ý[H•QNˆBˆ[ÙBˆˆ ËÑÈ Œ KL KLŒÂˆ ËÕšYY][™ÈHÐS•[š[X][Ûˆ\™HÈÙY\ÛÙHÛÛ\XÝY �]ÛÛ‰ÝÛܚˈIÛÛÛYH˜XÚÈÈ\Ë‚ˆ ËÚYˆ -˜[Y[š[JÙ[‹[šJJ^ˆ ËÈ[�ÜÙ]Ø[š[JÙ[‹S’WÐÐS•  -Nˆ ËÈÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆ ËÈ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\ÈH ˆ ËßBˆBˆBˆBˆB‚ˆ™]\›ˆ™\Ý[ŸB‚‹ËÈØ\ÚÙ^H[[Ûˆ‹‚‹ËÈ Œ N L KLL‹Ë‹ËÈ™\XÙ\È[œ™XYX›HÚXÚ×ܘ[™ÙJ -HXXܛˈ�[œÈ[™]šYX[‹ËÈÚXÚȘ[™ÙH�[˜Ý[ۜț܈XXÚ^\È[™™]\›œÈ�YB‹ËÈYˆ\™Ù]\ÈÚ][ˆ˜[™ÙHÙˆS ‚˜›ÛÛÚXÚ×ܘ[™ÙWÝ\™Ù]Ø[ -ÛÛœÝ[�]H -™[� ÛÛœÝ[�]H -�\™Ù] ÛÛœÝ[š[X][Û—ÚYÝ[š[X][Û—ÚY [� �ݘ[™ÙWÛZ[‹[� �ݘ[™ÙWÛX^ -BžÂˆ ËÈ]\Ý]™HH˜[Y\™Ù][�]K‚ˆYŠ]\™Ù] -Bˆˆ™]\›ˆ˜[ÙNˆB‚ˆ ËÈÙ]Ú[�\ˆÈ[š[X][Û‹‚ˆÛÛœÝר[š[H -˜[š[X][ÛˆH[� O›[Ù[]K˜[š[X][Û–Ø[š[X][Û—ÚYN‚ˆYˆ -XÚXÚ×ܘ[™ÙWÝ\™Ù]Þ -[� \™Ù] [š[X][Û‹˜[™ÙWÛZ[‹˜[™ÙWÛX^ -JBˆˆ™]\›ˆ˜[ÙNˆB‚ˆYˆ -XÚXÚ×ܘ[™ÙWÝ\™Ù]ÞJ[� \™Ù] [š[X][Û‹˜[™ÙWÛZ[‹˜[™ÙWÛX^ -JBˆˆ™]\›ˆ˜[ÙNˆB‚ˆYˆ -XÚXÚ×ܘ[™ÙWÝ\™Ù]ÞŠ[� \™Ù] [š[X][Û‹˜[™ÙWÛZ[‹˜[™ÙWÛX^ -JBˆˆ™]\›ˆ˜[ÙNˆB‚ˆYˆ -XÚXÚ×ܘ[™ÙWÝ\™Ù]ؘ\ÙJ[� \™Ù] [š[X][Û‹˜[™ÙWÛZ[‹˜[™ÙWÛX^ -JBˆˆ™]\›ˆ˜[ÙNˆB‚ˆ™]\›ˆ�YNŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ N L KLL‚‹Ë‹ËÈ™]\›ˆ�YHYˆ\™Ù]\ÈÚ][ˆ˜\ÙH˜[™ÙB‹ËÈÙˆ[�]IÜÈ[š[X][Û‹‚˜›ÛÛÚXÚ×ܘ[™ÙWÝ\™Ù]ؘ\ÙJÛÛœÝ[�]H -˜XÝ[™×Ù[�]KÛÛœÝ[�]H -�\™Ù] ÛÛœÝר[š[H -˜[š[X][Û‹[� �ݘ[™ÙWÛZ[‹[� �ݘ[™ÙWÛX^ -BžÂˆ[�[�ؘ\ÙNˆ[�\™Ù]ؘ\ÙN‚ˆ×ÛY]šX×ܘ[™ÙH˜[™ÙN‚ˆ ËÈ]\Ý]™HH\™Ù] ‚ˆYŠXXÝ[™×Ù[�]H]\™Ù] -Bˆˆ™]\›ˆ˜[ÙNˆB‚ˆ˜[™ÙK›X^H˜[™ÙWÛX^ˆ˜[™ÙK›Z[ˆH˜[™ÙWÛZ[ŽÂ‚ˆYˆ -[š[X][ÛŠBˆˆ˜[™ÙK›X^ -ÏH[š[X][Û‹Oœ˜[™ÙK˜˜\ÙK›X^ˆ˜[™ÙK›Z[ˆ -ÏH[š[X][Û‹Oœ˜[™ÙK˜˜\ÙK›Z[ŽÂˆB‚ˆ ËÈÙ]ÜÚ][ÛœÈØ\Ý\È[�YÙ\œË‚ˆ[�ؘ\ÙHH -[� -XXÝ[™×Ù[�]KO˜˜\ÙNˆ\™Ù]ؘ\ÙHH -[� -]\™Ù] O˜˜\ÙN‚ˆ ËÈÝX�˜XÝ[�]H˜\ÙHÜÚ][Ûˆœ›ÛH\™Ù]ÜÚ][Û‹‚ˆ\™Ù]ؘ\ÙH OH[�ؘ\ÙN‚ˆ ËÈ™]\›ˆ�YHYˆš[˜[\™Ù]ØØ][Ûˆ\ˆ ËÈÚ][ˆ˜[™ÙHZ[ˆ[™X^ ‚ˆ™]\›ˆ -\™Ù]ؘ\ÙH�H˜[™ÙK›Z[‚ˆ ‰ˆ\™Ù]ؘ\ÙHH˜[™ÙK›X^ -HÈ�YHˆ˜[ÙNŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ N L KLL‹Ë‹ËÈ™]\›ˆ�YHYˆ\™Ù]\ÈÚ][ˆ˜[™ÙB‹ËÈÙˆ[�]IÜÈ[š[X][Û‹‚˜›ÛÛÚXÚ×ܘ[™ÙWÝ\™Ù]Þ -ÛÛœÝ[�]H -˜XÝ[™×Ù[�]KÛÛœÝ[�]H -�\™Ù] ÛÛœÝר[š[H -˜[š[X][Û‹[� �ݘ[™ÙWÛZ[‹[� �ݘ[™ÙWÛX^ -BžÂˆ ËÈ]\Ý]™H[�]Y\Ë‚ˆYŠXXÝ[™×Ù[�]H]\™Ù] -Bˆˆ™]\›ˆ˜[ÙNˆB‚ˆ ËÈÙ]ÜÚ][ÛœÈØ\Ý\È[�YÙ\œË‚ˆÛÛœÝ[�[�ÞH -[� -XXÝ[™×Ù[�]KOœÜÚ][Û‹žˆÛÛœÝ[�\™Ù]ÞH -[� -]\™Ù] OœÜÚ][Û‹ž‚ˆ×ÛY]šX×ܘ[™ÙH˜[™ÙN‚ˆ˜[™ÙK›X^H˜[™ÙWÛX^ˆ˜[™ÙK›Z[ˆH˜[™ÙWÛZ[ŽÂ‚ˆYˆ -[š[X][ÛŠBˆˆ˜[™ÙK›X^ -ÏH[š[X][Û‹Oœ˜[™ÙKž ›X^ˆ˜[™ÙK›Z[ˆ -ÏH[š[X][Û‹Oœ˜[™ÙKž ›Z[ŽÂˆB‚ˆ ËÈ™]\›ˆ�YHYˆš[˜[\™Ù]ØØ][Ûˆ\ˆ ËÈÚ][ˆ˜[™ÙHZ[ˆ[™X^ ˆ˜[™ÙHÛÛ\\š\ÛÛ‚ˆ ËÈ\È™]™\œÙYÚ[ˆ[�]H˜XÙ\ÈY� ‚ˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -Bˆˆ ËÈY˜[™ÙHÈ[�]HÜÚ][Û‚ˆ Ëț܈š[˜[˜[™ÙHÛÛÜ™[˜]\Ë‚ˆ˜[™ÙK›Z[ˆH[�Þ -Ș[™ÙK›Z[ŽÂˆ˜[™ÙK›X^H[�Þ -Ș[™ÙK›X^‚ˆ™]\›ˆ -\™Ù]Þ�H˜[™ÙK›Z[‚ˆ ‰ˆ\™Ù]ÞH˜[™ÙK›X^ -HÈ�YHˆ˜[ÙNˆBˆ[ÙBˆˆ ËÈÝX�˜Xݘ[™ÙHœ›ÛH[�]Hˆ ËÈÜÚ][Ûˆ›Üˆš[˜[˜[™ÙHÛÛÜ™[˜]\Ë‚ˆ˜[™ÙK›Z[ˆH[�Þ H˜[™ÙK›Z[ŽÂˆ˜[™ÙK›X^H[�Þ H˜[™ÙK›X^‚ˆ™]\›ˆ -\™Ù]ÞH˜[™ÙK›Z[‚ˆ ‰ˆ\™Ù]Þ�H˜[™ÙK›X^ -HÈ�YHˆ˜[ÙNˆBŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ N L KLL‹Ë‹ËÈ™]\›ˆ�YHYˆ\™Ù]\ÈÚ][ˆH˜[™ÙB‹ËÈÙˆ[�]IÜÈ[š[X][Û‹‚˜›ÛÛÚXÚ×ܘ[™ÙWÝ\™Ù]ÞJÛÛœÝ[�]H -˜XÝ[™×Ù[�]KÛÛœÝ[�]H -�\™Ù] ÛÛœÝר[š[H -˜[š[X][Û‹[� �ݘ[™ÙWÛZ[‹[� �ݘ[™ÙWÛX^ -BžÂˆ ËÈ]\Ý]™HH\™Ù] ‚ˆYŠXXÝ[™×Ù[�]H]\™Ù] -Bˆˆ™]\›ˆ˜[ÙNˆB‚ˆ×ÛY]šX×ܘ[™ÙH˜[™ÙN‚ˆ˜[™ÙK›X^H˜[™ÙWÛX^ˆ˜[™ÙK›Z[ˆH˜[™ÙWÛZ[ŽÂ‚ˆYˆ -[š[X][ÛŠBˆˆ˜[™ÙK›X^ -ÏH[š[X][Û‹Oœ˜[™ÙKžK›X^ˆ˜[™ÙK›Z[ˆ -ÏH[š[X][Û‹Oœ˜[™ÙKžK›Z[ŽÂˆB‚ˆ ËÈÙ]ÜÚ][ÛœÈØ\Ý\È[�YÙ\œË‚ˆÛÛœÝ[�[�ÞHH -[� -XXÝ[™×Ù[�]KOœÜÚ][Û‹žNˆÛÛœÝ[�\™Ù]ÞHH -[� -]\™Ù] OœÜÚ][Û‹žH H[�ÞN‚ˆ ËÈ™]\›ˆ�YHYˆš[˜[\™Ù]ØØ][Ûˆ\ˆ ËÈÚ][ˆ˜[™ÙHZ[ˆ[™X^ ‚ˆ™]\›ˆ -\™Ù]ÞH�H˜[™ÙK›Z[‚ˆ ‰ˆ\™Ù]ÞHH˜[™ÙK›X^ -HÈ�YHˆ˜[ÙNŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ N L KLL‚‹Ë‹ËÈ™]\›ˆ�YHYˆ\™Ù]\ÈÚ][ˆˆ˜[™ÙB‹ËÈÙˆ[�]IÜÈ[š[X][Û‹‚˜›ÛÛÚXÚ×ܘ[™ÙWÝ\™Ù]ÞŠÛÛœÝ[�]H -ˆXÝ[™×Ù[�]KÛÛœÝ[�]H -�\™Ù] ÛÛœÝר[š[H -˜[š[X][Û‹[� �ݘ[™ÙWÛZ[‹[� �ݘ[™ÙWÛX^ -BžÂˆ ËÈ]\Ý]™HH\™Ù] ‚ˆYŠXXÝ[™×Ù[�]H]\™Ù] -Bˆˆ™]\›ˆ˜[ÙNˆB‚ˆ×ÛY]šX×ܘ[™ÙH˜[™ÙN‚ˆ˜[™ÙK›Z[ˆH˜[™ÙWÛZ[ŽÂˆ˜[™ÙK›X^H˜[™ÙWÛX^‚ˆYˆ -[š[X][ÛŠBˆˆ˜[™ÙK›X^ -ÏH[š[X][Û‹Oœ˜[™ÙKž‹›X^ˆ˜[™ÙK›Z[ˆ -ÏH[š[X][Û‹Oœ˜[™ÙKž‹›Z[ŽÂˆB‚ˆ ËÈÙ]ÜÚ][ÛœÈØ\Ý\È[�YÙ\œË‚ˆÛÛœÝ[�[�ÞˆH -[� -XXÝ[™×Ù[�]KOœÜÚ][Û‹žŽÂˆÛÛœÝ[�\™Ù]ÞˆH -[� -]\™Ù] OœÜÚ][Û‹žˆ H[�ގ‚ˆ ËÈ™]\›ˆ�YHYˆš[˜[\™Ù]ØØ][Ûˆ\ˆ ËÈÚ][ˆ˜[™ÙHZ[ˆ[™X^ ‚ˆ™]\›ˆ -\™Ù]Þˆ�H˜[™ÙK›Z[‚ˆ ‰ˆ\™Ù]ÞˆH˜[™ÙK›X^ -HÈ�YHˆ˜[ÙNŸB‚š[�ÚXÚ×ÜÜXÚX[ - -BžÂˆ[�]H -™NˆYŠ -[]™[ O››ÜÜXÚX[]™[ O››ÜÜXÚX[OH ÊH ‰ˆ˜[Y[š[JÙ[‹S’WÔÔPÒPS -H ‰‚ˆ -ÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÒ S’WÔÔPÒPS -HÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÔÔPÒPS -JBˆ -BˆˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊÙ[ŠNˆY[\Ù] -Ù[‹O˜ÛÛX›ÜÝ\  Ú^™[ÙŠ -œÙ[‹O˜ÛÛX›ÜÝ\ -H -ˆ JN‚ˆHHÙ[‹O›[šÎˆYŠJBˆˆKO�ZÙXXÝ[ÛˆH•Sˆ[�Ý[›[šÊÙ[ŠNˆÙ]ÚYJJNˆB‚ˆYŠÙ[‹O›[Ù[]KœÛX\�›ÛXˆ ‰ˆ\Ù[‹O›[Ù[]K™Ùœ™Y^™JBˆˆÛX\�Ø›ÛXŠÙ[‹Ù[‹O›[Ù[]KœÛX\�›ÛXŠNÈ ËÈÈÛX\�›ÛXˆ[[YYX][HYˆ]Ù\ۉݜ™Y^™HØÜ™Y[‚ˆB‚ˆÙ[‹Oœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NÈ ËÈYˆÜXÚX[\È^XÝ]YÚ[H�[›š[™ËÙX\Ù\ÈÈ�[‚ˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JÙ[‹S’WÔÔPÒPS  -N‚ˆYŠÙ[‹O›[Ù[]K™Ùœ™Y^™JBˆˆÛX\�›ÛX™\ˆHÙ[ŽÈ ËÈœ™Y^™\ÈH[š[X][ÛœÈÙˆ[[™[ZY\ËÜ^Y\œÈÚ[HÜXÚX[\È^XÝ]YˆB‚ˆ ËÈܘ]\È - L LŒ ŒJH›ÝÈHš[™š[š]HX[ÚX]ˆY™™XÝÈ^Y\œÈÛ›K›Ý[™[ZY\È܈œˆ ËÈ[™›ÝÈHš[™š[š]HX[ÚX]ˆÚ[Û›HÛÜšÈÚ[ˆHÛÜÝ\ÈPS Ú[›ÝÛÜšÈÚ[ˆHÛÜÝ\ÈT[ž[[Ü™BˆYŠÙ[‹O›[Ù[]K�\H ˆTWÔVQTŠBˆˆYŠ[›ØÛÜÝ -BˆˆYŠJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÒPSÐPÕU‘JJBˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÔÔPÒPS -JBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈Bˆ[ÙBˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆ[ÙBˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÔÔPÒPS -JBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆBˆBˆ[ÙBˆˆYŠ[›ØÛÜÝ -BˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÔÔPÒPS -JBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈Bˆ[ÙBˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆBˆ™]\›ˆ NˆBˆ™]\›ˆ ŸB‚‹ËÈÚXÚÈÙ^\ț܈ÜXÚX[[Ý™Kˆ\ÙYÙ]™\˜[[Y\ËÛÈH�[˜ÉÙ] ‚‹ËÈܘ]\È - L LŒ ŒJHYY™]È›YÜÈÈ\ÙHÚ][›Ý\ˆUPÒÈÈÙ^\È\È[ˆ™]È[\›˜]]™Bš[�^Y\—ØÚXÚ×ÜÜXÚX[ - -BžÂˆÙ^WÛX\Ú×ÝZÙ^HH ˆÙ^WÛX\Ú×Ý^Y\—ÚÙ^\ÈH^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\΂ˆÝÚ]Ú -Ûؘ[ØÛÛ™šY˘ZœÜXÚX[ -BˆˆØ\ÙHR”ÔPÒPSÒÑVWÔÔPÒPS‚ˆˆYˆ -^Y\—ÚÙ^\È ˆ“Q×ÔÔPÒPS -BˆˆZÙ^HH“Q×ÔÔPÒPSˆH‚ˆœ™XZ΂ˆØ\ÙHR”ÔPÒPSÒÑVWÑÕP“N‚‚ˆYˆ -]˜[Y[š[JÙ[‹S’WГÐÒÊH ‰ˆ^Y\—ÚÙ^\È ˆ“Q×ÔÔPÒPS -BˆˆZÙ^HH“Q×ÔÔPÒPSˆBˆ[ÙHYˆ -^Y\—ÚÙ^\È ˆ“Q×Ò•ST ‰ˆ^Y\—ÚÙ^\È ˆ“Q×ÐUPÒÊBˆˆZÙ^HH“Q×Ò•ST“Q×ÐUPÒΈBˆœ™XZ΂ˆØ\ÙHR”ÔPÒPSÒÑVWÐUPÒÌŽ‚‚ˆYˆ -^Y\—ÚÙ^\È ˆ“Q×ÐUPÒÌŠBˆˆZÙ^HH“Q×ÐUPÒ̎ˆBˆœ™XZ΂ˆØ\ÙHR”ÔPÒPSÒÑVWÐUPÒÌ΂‚ˆYˆ -^Y\—ÚÙ^\È ˆ“Q×ÐUPÒÌÊBˆˆZÙ^HH“Q×ÐUPÒÌΈBˆœ™XZ΂ˆØ\ÙHR”ÔPÒPSÒÑVWÐUPÒÍ‚‚ˆYˆ -^Y\—ÚÙ^\È ˆ“Q×ÐUPÒÍ -BˆˆZÙ^HH“Q×ÐUPÒ͈Bˆœ™XZΈˆY˜][‚ˆZÙ^HH ˆB‚ˆYˆ -]ZÙ^JBˆˆ™]\›ˆ ˆB‚ˆYŠÚXÚ×ÜÜXÚX[ - -JBˆˆÙ[‹OœÝ[[YHH ˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H�ZÙ^Nˆ™]\›ˆ NˆBˆ[ÙBˆˆ™]\›ˆ ˆBŸB‚‚�›ÚYÛÛ[[Û—Û[™ - -BžÂˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆB‚ˆÙ[‹O�ZÙXXÝ[ÛˆH•SˆÙ]ÚYJÙ[ŠNŸB‚‚‹ËØ[š[X[�[ˆÚ[ˆ[ÝHÜÝ] È[Y\ÈžHZ[Â�›ÚY�[˜[š[X[ - -BžÂˆÛÛ[[Û—ÝØ[ר[š[JÙ[ŠNˆ ËÙ[�ÜÙ]Ø[š[JÙ[‹S’WÕÐSË -N‚ˆYŠÙ[‹OœÜÚ][Û‹žY˜[˜Ù^ H Ù[‹OœÜÚ][Û‹žˆY˜[˜Ù^ -È -šY[Û[Ù\Ëš™\È -È -JBˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÐS’SPSÔ•S—ÓÕUÓїГÕS‘ÊNˆ™]\›ŽÂˆB‚ˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÙ[‹OœÜÚ][Û‹ž -ÏHÙ[‹O›[Ù[]KœÜYY žˆBˆ[ÙBˆˆÙ[‹OœÜÚ][Û‹ž OHÙ[‹O›[Ù[]KœÜYY žˆBŸB‚‚�›ÚY^Y\—Ø›[šÊ -BžÂˆÙ[‹O˜›[šÈH NˆYŠÝ[YH�HÙ[‹OœÝ[[YJBˆˆ^Y\—ÙYJ -NˆBŸB‚‚�›ÚYÛÛ[[Û—ÙܘX˜]XÚÊ -BžÂˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆB‚ˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘N‚ˆYŠJÙ[‹O˜ÛÛX›ÜÝ\ÌHÙ[‹O˜ÛÛX›ÜÝ\ÌWHˆÙ[‹O˜ÛÛX›ÜÝ\Ì—HÙ[‹O˜ÛÛX›ÜÝ\Ì×HˆÙ[‹O˜ÛÛX›ÜÝ\ÍJJBˆˆ[�Ý[›[šÊÙ[ŠNˆB‚ˆYŠÙ[‹O›[šÊBˆˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙܘXŽÂˆÙ[‹O›[šËO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙܘX˜™YˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘Nˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P‹ -NˆÙ]ÜZ[ŠÙ[‹O›[šË LK -Nˆ\]WÙœ˜[YJÙ[‹Ù[‹O˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆ\]WÙœ˜[YJÙ[‹O›[šËÙ[‹O›[šËO˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆBˆ[ÙBˆˆÙ[‹O�ZÙXXÝ[ÛˆH•SˆY[\Ù] -Ù[‹O˜ÛÛX›ÜÝ\  Ú^™[ÙŠ -œÙ[‹O˜ÛÛX›ÜÝ\ -H -ˆ JNˆÙ]ÚYJÙ[ŠNˆBŸB‚‹ËÈ�[˜Ý[Ûˆ]Ø]\Ù\ÈH^Y\ˆÈÛÛ�[�YHÈ[Ý™H\܈ÝÛˆ[�[H[š[X][Ûˆ\Èš[š\ÚY^Z[™Â�›ÚYÛÛ[[Û—ÙÙÙJ -H ËÈ™]È�[˜Ý[ÛˆÛÈ^Y\œÈØ[ˆÙÙHÚ]\\܈ÝÛˆÝÛ‚žÂˆYŠÙ[‹O˜[š[X][™ÊH ËÈÛÛ�[�Y\ÈÈ[Ý™H\ÈÛ™È\ÈH^Y\ˆ\È[š[X][™Âˆˆ™]\›ŽÂˆBˆ[ÙH ËÈÛ˜ÙHÛ™H[š[X][™Ë™]\›œÈÈ[šÚ[™ÂˆˆÙ[‹O�ZÙXXÝ[ÛˆH•SˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆÙ]ÚYJÙ[ŠNˆBŸB‚‚�›ÚYÛÛ[[Û—Ü™YXÚÊ -BžÂˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆBˆÙXÚÊÙ[ŠNŸB‚‚�›ÚYÛÛ[[Û—ÚYJ -BžÂˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆBˆÙ[‹O�ZÙXXÝ[ÛˆH•SˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘NˆÙ[‹OšY[™ÈHQS‘×Ô‘TT‘QˆÛÛ[[Û—ÚYWØ[š[JÙ[ŠNŸB‚‚�›ÚYžYXÚÊ[�]H -™[� -BžÂˆ[� Oœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NˆYŠ˜[Y[š[J[� S’WÑPÒÒS‘ÊJBˆˆ[� O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ü™YXÚΈ[� O�™[ØÚ]KžH[� O�™[ØÚ]KžˆH ˆ[� O™XÚÚ[™ÈHPÒ×Ô‘TT‘Qˆ[� OšY[™ÈHQS‘×Ó“Ó‘Nˆ[�ÜÙ]Ø[š[J[� S’WÑPÒÒS‘Ë -NˆBˆ[ÙBˆˆÙXÚÊ[� -NˆBŸB‚‚�›ÚYžYXÚÜš\ÙJ[�]H -™[� -BžÂˆ[� Oœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NˆYŠ˜[Y[š[J[� S’WÑPÒÔ’TÑJJBˆˆ[� O�ZÙXXÝ[ÛˆHÛÛ[[Û—ÚYNˆ[� O�™[ØÚ]KžH[� O�™[ØÚ]KžˆH ˆ[� O™XÚÚ[™ÈHPÒ×Ô’TÑNˆ[� OšY[™ÈHQS‘×Ó“Ó‘Nˆ[�ÜÙ]Ø[š[J[� S’WÑPÒÔ’TÑK -NˆBˆ[ÙBˆˆ[� O�ZÙXXÝ[ÛˆH•Sˆ[� OšY[™ÈHQS‘×Ô‘TT‘QˆÛÛ[[Û—ÚYWØ[š[JÙ[ŠNˆBŸB‚‚�›ÚYÙXÚÊ[�]H -™[� -BžÂˆ[� O�ZÙXXÝ[ÛˆH•Sˆ[� O�™[ØÚ]KžH[� O�™[ØÚ]KžˆH ˆ[� O™XÚÚ[™ÈHPÒ×ÐPÕU‘Nˆ[� OšY[™ÈHQS‘×Ô‘TT‘Qˆ[�ÜÙ]Ø[š[J[� S’WÑPÒË -NŸB‚‚�›ÚYÛÛ[[Û—Ü™Z�[\ - -BžÂˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆBˆÚ�[\ -Ù[‹Oš�[\ �™[ØÚ]KžKÙ[‹Oš�[\ �™[ØÚ]Kž Ù[‹Oš�[\ �™[ØÚ]Kž‹Ù[‹Oš�[\ ˜[š[X][Û—ÚY -NŸB‚‚�›ÚYžZ�[\ -›Ø]�[\‹›Ø]�[\ ›Ø]�[\‹[š[X][Û—ÚYÝ[š[X][Û—ÚY -BžÂˆÙ[‹Oš�[\ �™[ØÚ]KžHH�[\ŽÂˆÙ[‹Oš�[\ �™[ØÚ]KžH�[\ˆÙ[‹Oš�[\ �™[ØÚ]KžˆH�[\ŽÂˆÙ[‹Oš�[\ ˜[š[X][Û—ÚYH[š[X][Û—ÚY‚ˆÙ[‹O™XÚÚ[™ÈHPÒ×Ó“Ó‘NˆYŠ˜[Y[š[JÙ[‹S’WÒ•STSVJJBˆˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ü™Z�[\ˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ‚ˆÙ[‹OšY[™ÈHQS‘×Ó“Ó‘Nˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STSVK -NˆBˆ[ÙBˆˆÚ�[\ -�[\‹�[\ �[\‹[š[X][Û—ÚY -NˆBŸB‚‚�›ÚYÚ�[\ -›Ø]�[\‹›Ø]�[\ ›Ø]�[\‹[š[X][Û—ÚYÝ[š[X][Û—ÚY -BžÂˆ[�]H -™\Ý‚ˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ú�[\‚ˆYŠÛؘ[ÜØ[\WÛ\Ý š�[\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý š�[\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ËÔÜ]Ûˆ�[\Ý\�\Ý ‚ˆYŠÙ[‹O›[Ù[]K™\Ý š�[\ÜÝ\��H -Bˆˆ\ÝHÜ]ÛŠÙ[‹OœÜÚ][Û‹ž Ù[‹OœÜÚ][Û‹ž‹Ù[‹OœÜÚ][Û‹žKÙ[‹O™\™XÝ[Û‹•S Ù[‹O›[Ù[]K™\Ý š�[\ÜÝ\� •S -NˆYŠ\Ý -Bˆˆ\Ý OœÜ]Û�\HHÔUÓ—ÕTWÑTÕÒ•STˆ\Ý O˜˜\ÙHHÙ[‹OœÜÚ][Û‹žNˆ\Ý O˜]]ÚÚ[HUUÒÒSÐS’SPUSÓ—ÐÓÓTUNˆ^XÝ]WÛÛœÜ]Û—ÜØÜš\ -\Ý -NˆBˆB‚ˆÙ]Ú�[\[™ÊÙ[ŠN‚ˆÜÜÊÙ[‹�[\ŠN‚ˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -BˆˆÙ[‹O�™[ØÚ]KžH Z�[\ˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžH�[\ˆB‚ˆÙ[‹O�™[ØÚ]KžˆH�[\ŽÂˆ[�ÜÙ]Ø[š[JÙ[‹[š[X][Û—ÚY  -NŸB‚‹ËÈ�[˜Ý[ۈܙX]YÈÛÛXš[™HHXÝ[ÛˆZÙ[ˆYˆZ]\ˆXÚÚ[™È\[ˆ][K܈�[›š[™È[�È[ˆ][H]\ÈB‹ËÈÕP•TWÕÕPÒ ^XÝ][™ÈH\›ÜšX]HXÝ[Ûˆ˜\ÙYÛˆÚXÚ\HÙˆ][H\ÈXÚÙY\�›ÚYYš[™Ú][J[�]H -›Ý\ŠBžÂˆ ËÈ�[˜Ý[Ûˆ]ZÙ\ÈØ\™HÙˆ][\ÈÚ[ˆXÚÙY\ˆÙ]ÛÜÛ™[� -Ù[‹Ý\ŠN‚ˆ Ê‚ˆ -ˆYØXÞH[Z]Y\ÙH™\[š\Ú ‚ˆ -ˆˆ -ˆYˆ][H\ÈH\ÙWØY˜[YH[™ÛÛXÝ[™Âˆ -ˆ[�]H\ÈH[Z]Y\ÙHÙX\Û‹ÙHYˆ -ˆ\ÙWØYÈ\Ù\È™[XZ[š[™Ë‚ˆ -‹Â‚ˆYŠÝ\‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØY -BˆˆYŠÙ[‹O�ÙX\[� ‰ˆÙ[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÓSRUQÕTÑJBˆˆÙ[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØÛÝ[� -ÏHÝ\‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØY‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙBˆˆYØÛÜ™JÙ[‹Oœ^Y\š[™^ Ý\‹O›[Ù[]KœØÛÜ™JNˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆB‚ˆ ËÙ[™ÙˆÙX\ÛœÈ][\ÈÙXÝ[Û‚ˆ[ÙHYŠÝ\‹O›[Ù[]KœØÛÜ™JBˆˆYØÛÜ™JÙ[‹Oœ^Y\š[™^ Ý\‹O›[Ù[]KœØÛÜ™JNˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� -BˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� -ÏHÝ\‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�‚ˆYŠÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆÙ[‹O›[Ù[]KšX[ -BˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�HÙ[‹O›[Ù[]KšX[ˆB‚ˆÝ\‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]K›\ -BˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� -ÏHÝ\‹O›[Ù[]K›\‚ˆYŠÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�ˆÙ[‹O›[Ù[]K›\ -BˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�HÙ[‹O›[Ù[]K›\ˆB‚ˆÝ\‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�H ˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆ[ÙHYŠÝšXÛ\ -Ý\‹O›[Ù[]K›˜[YK•[YHŠHOH -Bˆˆ[Y[Y�H]™[ OœÙ][YH -ˆÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYYÈ ËÈ™Xˆ �  Œ H H\È[™H[Ý™Y\™HÈÙ]Ý\ÝÛH[YB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]K›XZÙZ[�ŠBˆˆ ËÈX\ˆ ‹ Œ H H™]È][HXZÙ\È^Y\ˆ[�š[˜ÚX›BˆÙ[‹Oš[�š[˜ÚX›HHS•’S�ÒP“WÒS•S‘ÒP“NˆÙ[‹Oš[�š[˜Ý[YHHÝ[YH -ÈP”ÊÝ\‹O›[Ù[]K›XZÙZ[�ŠNˆÙ[‹O˜›[šÈH -Ý\‹O›[Ù[]K›XZÙZ[�ˆˆ -N‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]KœÛX\�›ÛXŠBˆˆ ËÈ[XYÙ\È]™\ž][™ÈÛˆHØÜ™Y[‚ˆÛX\�Ø›ÛXŠÙ[‹Ý\‹O›[Ù[]KœÛX\�›ÛXŠN‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]KœÝX�\HOHÕP•TWÕÑPTÓŠBˆˆ›ÜÙX\ÛŠÙ[‹ -NˆÙ[‹O�ÙX\[�HÝ\ŽÂˆÙ]ÝÙX\ÛŠÙ[‹Ý\‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—Ú[™^  -N‚ˆ ʈ[Ý™HÈÙX\ÛˆØØ][ÛˆYˆ] ÜÈ[ˆ˜[š[X[‹ˆ -‹ÂˆYŠÙ[‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÐS’SPS -BˆˆÙ[‹O™\™XÝ[ÛˆHÝ\‹O™\™XÝ[ێˆÙ[‹OœÜÚ][Û‹žHÝ\‹OœÜÚ][Û‹žˆÙ[‹OœÜÚ][Û‹žˆHÝ\‹OœÜÚ][Û‹žŽÂˆB‚ˆYŠJÝ\‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÓSRUQÕTÑJH ‰ˆÙ[‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÓSRUQÕTÑJBˆˆÝ\‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]HHÑPTÓ—ÔÕUWÓSRUQÕTÑNˆB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]KœÝX�\HOHÕP•TWÔ“Ò‘PÕSJBˆˆ›ÜÙX\ÛŠÙ[‹ -NˆÙ[‹O�ÙX\[�HÝ\ŽÂ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ™Ù]�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ™Ù]  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙHYŠÝ\‹O›[Ù[]K˜Ü™Y] -BˆˆYŠ[›ÜÚ\™JBˆˆÜ™Y]ÊÊΈBˆ[ÙBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^K˜Ü™Y]ÊÊΈB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ›Û™WÝ\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ›Û™WÝ\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆ[ÙBˆˆ ËÈ]\Ý™HH ]\[‹‚ˆ^Y\–ÜÙ[‹Oœ^Y\š[™^K›]™\ÊÊ΂ˆYŠÛؘ[ÜØ[\WÛ\Ý ›Û™WÝ\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ›Û™WÝ\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆB‚ˆYŠÝ\‹O›[Ù[]KœÝX�\HOHÕP•TWÕÑPTÓˆ ‰ˆÝ\‹O›[Ù[]KœÝX�\HOHÕP•TWÔ“Ò‘PÕSJBˆˆÝ\‹O�ZÙXXÝ[ÛˆHÝZXÚYNˆYŠ[Ý\‹O›[Ù[]Kš[œÝ[�][YX] -BˆˆÝ\‹O›™^[šÈHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ΈBˆBˆ[ÙBˆˆYŠJÝ\‹O›™^[š[H ˆSVWÑ“Q×ÒS‘’S’UJJHˆÝ\‹O›™^[š[HH[š[X][Û—Ý[Y\Ý[\ØYØ›Ý[™Y -ˆÝ[YKˆ[^WÛ][\WØ›Ý[™Y -ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY ˆRS• �ÐÊNNNNNJBˆ -Bˆ -NˆBˆÝ\‹O›™^[šÈHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆNNNNNNˆBˆÝ\‹OœÜÚ][Û‹žˆHUSWÒQWÔÔÒUSӗ֎ŸB‚�›ÚY^Y\—Ù˜[ØÚXÚÊ -BžÂˆYŠ]]Û[™OH ˆ ‰ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×Ò•ST -JHOH -“Q×ÓSÕ‘UT“Q×Ò•ST -JBˆ‚BKËÈX\šÈ] ÛÈÙHÚ[^H[™[š[X][ÛˆÚ[ˆ]HÜ›Ý[™ ‚ˆÙ[‹O™[XYÙWÛÛ—Û[™[™Ë˜]XÚ×ٛܘÙHHUPÒ×Ñ“Ô�ÑWÓS‘ÐÓÓSPS‘ȈBŸB‚�›ÚY^Y\—ÙܘX—ØÚXÚÊ -BžÂˆ[�]H -›Ý\ˆHÙ[‹O›[šÎ‚ˆYŠÝ\ˆOH•S -Ù[‹O›[Ù[]K™Ü˜X™š[š\Ú ‰ˆÙ[‹O˜[š[X][™È ‰ˆ\Ù[‹O™Ü˜X�Ø[Ú[™ÊJBˆˆ™]\›ŽÂˆB‚ˆYŠÙ[‹O˜˜\ÙHOHÝ\‹O˜˜\ÙJBˆˆ ËÈÚ[™ÙH\Èœ›ÛH OœÜÚ][Û‹žHÈ O˜˜\ÙBˆÙ[‹O�ZÙXXÝ[ÛˆH•Sˆ[�Ý[›[šÊÙ[ŠNˆÙ]ÚYJÙ[ŠNˆ™]\›ŽÂˆB‚ˆYŠ^Y\—ØÚXÚ×ÜÜXÚX[ - -JBˆˆ™]\›ŽÂˆB‚ˆYŠ[›ÛÜÝ ‰ˆÙ[‹O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜרÛÛ™][Ûˆ ˆÑPTÓ—ÓÔÔ×ÐÓÓ‘USÓ—ÑÔ�P�’S‘ÊBˆˆ›ÜÙX\ÛŠÙ[‹ JNˆB‚ˆ ËÈܘX�\›ˆÛÙBˆYŠÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•T“—JBˆˆ ËÈÝ[\›š[™ÏÈۉݛÝ\ˆÚ][ž][™È[ÙBˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆB‚ˆ ËÈÛ™H\›š[™ÏÈÝÚ]Ú\™XÝ[ÛœÈ[™™]\›ˆÈܘXˆ[š[X][Û‚ˆ[ÙBˆˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆÝ\‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆ[ÙBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆÝ\‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆÝ\‹OœÜÚ][Û‹žHÙ[‹OœÜÚ][Û‹ž -È - - -Ù[‹O™\™XÝ[Ûˆ -ˆ ŠH H JH -ˆÙ[‹O›[Ù[]K™Ü˜X™\Ý[˜ÙJNˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P‹ -NˆÙ]ÜZ[ŠÝ\‹ LK -Nˆ\]WÙœ˜[YJÙ[‹Ù[‹O˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆ\]WÙœ˜[YJÝ\‹Ý\‹O˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆBˆB‚ˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘NÈ Ëٛ܈ÚXÚÚ[™ÂˆÙ[‹O™Ü˜X�Ø[Ú[™ÈH ‚‚KËÈ[Ý™HÙ^HÜÜÚ]HœËˆXÜ™][Û�ˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒÈ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -Hˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆ ËÈ[š]X][™ÈܘX�\›‚ˆYŠÙ[‹O›[Ù[]K™Ü˜X�\›ŠBˆˆ ËÈÝ\�[š[X][ÛˆYˆ]^\ÝË‹‹‚ˆYŠ˜[Y[š[JÙ[‹S’WÑÔ�P•T“ŠJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P•T“‹ -NˆYŠ˜[Y[š[JÝ\‹S’WÑÔ�P�‘QT“ŠJBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘QT“‹ -NˆBˆ[ÙHYŠ˜[Y[š[JÝ\‹S’WÑÔ�P�‘Q -JBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘Q  -NˆBˆ[ÙBˆˆYˆ - - -[Ý\‹O™\™XÝ[Ûˆ ‰ˆÙ[‹OœÜÚ][Û‹žˆÝ\‹OœÜÚ][Û‹ž -H -Ý\‹O™\™XÝ[Ûˆ ‰ˆÙ[‹OœÜÚ][Û‹žÝ\‹OœÜÚ][Û‹ž -JH ‰ˆ˜[Y[š[JÝ\‹S’WÐ�PÒÔRSŠH -H[�ÜÙ]Ø[š[JÝ\‹S’WÐ�PÒÔRS‹ -Nˆ[ÙH[�ÜÙ]Ø[š[JÝ\‹S’WÔRS‹ -NˆBˆÝ\‹O�™[ØÚ]KžHÝ\‹O�™[ØÚ]KžˆHÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆÝ\‹OœÜÚ][Û‹žHÙ[‹OœÜÚ][Û‹žˆ™]\›ŽÂˆB‚ˆ ËÈÝ\�Ú\ÙK�\Ý\›ˆ\›Ý[™ˆ[ÙBˆˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆÝ\‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆ[ÙBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆÝ\‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P‹ -NˆÙ]ÜZ[ŠÝ\‹ LK -Nˆ\]WÙœ˜[YJÙ[‹Ù[‹O˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆ\]WÙœ˜[YJÝ\‹Ý\‹O˜[š[X][Û‹O›�[Yœ˜[Y\È H JNˆÝ\‹OœÜÚ][Û‹žHÙ[‹OœÜÚ][Û‹ž -È - - -Ù[‹O™\™XÝ[Ûˆ -ˆ ŠH H JH -ˆÙ[‹O›[Ù[]K™Ü˜X™\Ý[˜ÙJNˆBˆBˆ[ÙHYŠ]˜[Y[š[JÙ[‹S’WÑÔ�P•ÐSÊH ‰ˆÝ[YHˆÙ[‹Oœ™[X\Ù][YJBˆˆ ËÈ™[X\ÙBˆÙ[‹O�ZÙXXÝ[ÛˆH•Sˆ[�Ý[›[šÊÙ[ŠNˆÙ]ÚYJÙ[ŠNˆ™]\›ŽÂˆBˆBˆ[ÙBˆˆÙ[‹Oœ™[X\Ù][YHHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È ŠNˆB‚ˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰‚ˆ -Ù[‹O™\™XÝ[Ûˆˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H‚ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JJBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È OH“Q×ÐUPÒΈYŠ˜[Y[š[JÙ[‹S’WÑÔ�P��PÒÕÐT‘ -JBˆˆÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÐ�PÒÕÐT‘ -NˆBˆ[ÙHYŠ˜[Y[š[JÙ[‹S’WÕ“ÕÊJBˆˆYŠÙ[‹O›[Ù[]K�›ÝÙœ˜[Y]ØZ]�H -BˆˆÜ™]›ÝÊ -NˆBˆ[ÙBˆˆÝ›ÝÊ -NˆBˆBˆ[ÙBˆˆÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÐUPÒÊNˆBˆBˆ ËÈܘXˆ›Ü�Ø\™ˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰‚ˆ˜[Y[š[JÙ[‹S’WÑÔ�P‘“Ô•ÐT‘ -H ‰‚ˆ -\Ù[‹O™\™XÝ[Ûˆˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H‚ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JJBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕѓԕÐT‘ -NˆBˆ ËÈܘXˆ\ˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰‚ˆ˜[Y[š[JÙ[‹S’WÑÔ�P•T -H ‰ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -JBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÕT -NˆBˆ ËÈܘXˆÝÛ‚ˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰‚ˆ˜[Y[š[JÙ[‹S’WÑÔ�P‘ÕÓŠH ‰ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠJBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÑÕÓŠNˆBˆ ËțܛX[ܘXˆ]XÚˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰ˆ˜[Y[š[JÙ[‹S’WÑÔ�P�UPÒÊJBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÐUPÒÊNˆB‚KËÈܘ]\È - L LŒ ŒJHYYH˜][[š[X][Û‚ˆ[ÙHYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×Ò•ST ‰ˆ˜[Y[š[JÙ[‹S’WÕ�US -JBˆˆ ËÈܘ]\È - ËLŒ ŒŠHš^YHŒË]˜][ȈÛÜ�YË›ÝÈÚ[^XÝ]HÛ˜ÙH\È[�[™Yˆ ËÈܘ]\È - KLŒ ŒÊHš^Y[ˆ\ÜÝYHÚ\™HܘX˜]XÚ̈[š[X][Ûˆ�[YšY\Ș][Yˆ›Ý\™HXÛ\™Yˆ ËÈܘ]\È - KLŒ ŒÊHYYÛÛYH˜]]™H™Z]š[Ý\œÂˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×Ò•STˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘NˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙܘX˜]XÚΈY[\Ù] -Ù[‹O˜ÛÛX›ÜÝ\  Ú^™[ÙŠ -œÙ[‹O˜ÛÛX›ÜÝ\ -H -ˆ JNˆ[�ÜÙ]Ø[š[JÙ[‹S’WÕ�US  -NˆBˆ ËÈܘXˆ]XÚÈš[š\Ú\‚ˆ[ÙHYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ -“Q×ÐUPÒÈ“Q×Ò•ST -JBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �HŠ“Q×ÐUPÒÈ“Q×Ò•ST -N‚ˆ ËÈ\™›Ü›Hš[˜[›݈YŠ˜[Y[š[JÙ[‹S’WÑÔ�P�UPÒÌŠH˜[Y[š[JÙ[‹S’WÐUPÒÌÊJBˆˆÙܘX˜]XÚÊÔ�P—ÐPÕSÓ—ÔÑSPÕÑ’S’TÒ -NˆBˆ[ÙBˆˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘NˆY[\Ù] -Ù[‹O˜ÛÛX›ÜÝ\  Ú^™[ÙŠ -œÙ[‹O˜ÛÛX›ÜÝ\ -H -ˆ JNˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙܘX˜]XÚΈžZ�[\ -Ù[‹O›[Ù[]Kš�[\ZYÚ Ù[‹O›[Ù[]Kš�[\ÜYY  S’WÒ•ST -NˆBˆB‚ˆ ËÈܘXˆØ[ÈÛÙBˆ[ÙHYŠ˜[Y[š[JÙ[‹S’WÑÔ�P•ÐSÊH ËÈÚXÚÈYˆܘX�Ø[È[š[X][Ûˆ^\Ý‚ˆ ËÈYˆ[�]H\ÈÝ[[š[X][™È[ž][™È™\ÚY\ÈHܘX�Ø[Ș\šX[� Û‰Ý][H[Ý™Bˆ ‰ˆ -\Ù[‹O˜[š[X][™ÈÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•ÐS×BˆÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•ÐSÕTBˆÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•ÐSÑÕÓ—BˆÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P��PÒÕÐS×JJBˆ‚ˆ ËȈ^\È[Ý™[Y[�ˆYŠVQT—ÓRS—ÖˆOHVQT—ÓPVÖŠBˆˆYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -BˆˆYŠÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY -BˆˆÙ[‹O�™[ØÚ]KžˆH \Ù[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY È ŽÂˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžˆH \Ù[‹O›[Ù[]KœÜYY ž È ŽÂˆBˆBˆ[ÙHYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆYŠÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY -BˆˆÙ[‹O�™[ØÚ]KžˆHÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY È ŽÂˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžˆHÙ[‹O›[Ù[]KœÜYY ž È ŽÂˆBˆBˆ[ÙHYŠJ^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓŠJJBˆˆÙ[‹O�™[ØÚ]KžˆH ˆBˆB‚ˆ ËÈ^\È[Ý™[Y[�ˆYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -BˆˆYŠÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY -BˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYYˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O›[Ù[]KœÜYY žˆBˆB‚ˆ[ÙHYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -BˆˆYŠÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYY -BˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O›[Ù[]K™Ü˜X�Ø[ÜÜYYˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O›[Ù[]KœÜYY žˆBˆBˆ[ÙHYŠJ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JH -BˆˆÙ[‹O�™[ØÚ]KžH ˆB‚ˆ ËÈÙ][™ÈH[š[X][ۜȘ\ÙYÛˆH™[ØÚ]HÙ]X›Ý™BˆYŠÙ[‹O�™[ØÚ]KžÙ[‹O�™[ØÚ]KžŠBˆˆYŠ - -Ù[‹O�™[ØÚ]Kžˆ  ‰ˆÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -H -Ù[‹O�™[ØÚ]Kž  ‰ˆÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -JH ‰ˆ˜[Y[š[JÙ[‹S’WÑÔ�P��PÒÕÐSÊJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P��PÒÕÐSË -NˆBˆ[ÙHYŠÙ[‹O�™[ØÚ]Kžˆ  ‰ˆ˜[Y[š[JÙ[‹S’WÑÔ�P•ÐSÕT -JBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P•ÐSÕT  -NˆBˆ[ÙHYŠÙ[‹O�™[ØÚ]Kžˆˆ  ‰ˆ˜[Y[š[JÙ[‹S’WÑÔ�P•ÐSÑÕÓŠJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P•ÐSÑÕÓ‹ -NˆBˆ[ÙBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P•ÐSË -NˆBˆYŠÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•ÐSÕTH ‰ˆ˜[Y[š[JÝ\‹S’WÑÔ�P�‘QÐSÕT -JBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘QÐSÕT  -NˆBˆ[ÙHYŠÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P•ÐSÑÕÓ—H ‰ˆ˜[Y[š[JÝ\‹S’WÑÔ�P�‘QÐSÑÕÓŠJBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘QÐSÑÕÓ‹ -NˆBˆ[ÙHYŠÙ[‹O˜[š[X][ÛˆOHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÑÔ�P��PÒÕÐS×H ‰ˆ˜[Y[š[JÝ\‹S’WÑÔ�P�‘Q�PÒÕÐSÊJBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘Q�PÒÕÐSË -NˆBˆ[ÙHYŠ˜[Y[š[JÝ\‹S’WÑÔ�P�‘QÐSÊJBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘QÐSË -NˆBˆ[ÙHYˆ -˜[Y[š[JÝ\‹S’WÑÔ�P�‘Q -JBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘Q  -NˆBˆ[ÙBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÔRS‹ -NˆBˆBˆ[ÙBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÑÔ�P‹ -NˆYˆ -˜[Y[š[JÝ\‹S’WÑÔ�P�‘Q -JBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÑÔ�P�‘Q  -NˆBˆ[ÙBˆˆ[�ÜÙ]Ø[š[JÝ\‹S’WÔRS‹ -NˆBˆBˆ ËÈ\ÙHÚXÚ×Û[š×Û[Ý™HÈÙ]™[ØÚ]KÛ‰ÝÚ[™ÙH]\™BˆÝ\‹O�™[ØÚ]KžˆHÝ\‹O�™[ØÚ]KžH ˆÙ[‹O™Ü˜X�Ø[Ú[™ÈH NˆB‚ˆYŠÙ[‹O˜]XÚÚ[™ÈOHUPÒÒS‘×Ó“Ó‘JBˆˆÙ[‹Oœ™[X\Ù][YHHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È ŠNÈ ËÈ™\Ù]™[X\Ù][YHÚ[ˆÈÛÛ\Ú[Û‚ˆBŸB‚�›ÚY^Y\—ÝØ[ÛÙ™—ØÚXÚÊ -BžÂˆ Ê‚ˆ -ˆ\›‹ˆˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—ÕT“ŠBˆˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆB‚ˆ Ê‚ˆ -ˆÝÜ ˆYˆ›ÝÛ[™ÈHY�Ü‚ˆ -ˆšYÚÙ^KÚ[Üš^›Û�[[ÛY[�[K‚ˆ -‹ÂˆYˆ -Ù[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—ÖÔÕÔ -BˆˆYˆ -J^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -JJBˆˆYˆ -Ù[‹O�™[ØÚ]KžOH -BˆˆÙ[‹O�™[ØÚ]Kž -�HRT—ÐÓÓ•“ÓÔÕÔÑ�PÕԎˆBˆBˆB‚ˆ Ê‚ˆ -ˆˆÝÜ ˆYˆ›ÝÛ[™È[ˆ\Ü‚ˆ -ˆÝÛˆÙ^KÚ[]\˜[[ÛY[�[K‚ˆ -‹ÂˆYˆ -Ù[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—Ö—ÔÕÔ -BˆˆYˆ -J^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓŠJJBˆˆYˆ -Ù[‹O�™[ØÚ]KžˆOH -BˆˆÙ[‹O�™[ØÚ]Kžˆ -�HRT—ÐÓÓ•“ÓÔÕÔÑ�PÕԎˆBˆBˆB‚ˆ ʈÜš^›Û�[[Ý™HÛÛ�›Û -Yˆ[™XYH[Ýš[™ÊKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—ÖÐQ•TÕ -H ËÛ[Ý™OˆˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆÙ[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O�™[ØÚ]KžˆBˆB‚ˆ ʈÜš^›Û�[[Ý™HÛÛ�›Û -[žHØ[ÛÙ™ŠKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—ÖÓSÕ‘JBˆˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆÙ[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O�™[ØÚ]KžˆB‚ˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆ -\Ù[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]Kž OHÙ[‹O›[Ù[]KœÜYY žˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆ -\Ù[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O›[Ù[]KœÜYY žˆBˆB‚ˆ ʈˆ[Ý™HÛÛ�›Û -Yˆ[™XYH[Ýš[™ÊKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—Ö—ÐQ•TÕ -BˆˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆÙ[‹O�™[ØÚ]Kžˆˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆÙ[‹O�™[ØÚ]Kžˆ -JBˆˆÙ[‹O�™[ØÚ]KžˆH \Ù[‹O�™[ØÚ]KžŽÂˆBˆB‚ˆ ʈˆ[Ý™HÛÛ�›Û -[žHØ[ÛÙ™ŠKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÕÐSÓÑ‘—Ö—ÓSÕ‘JBˆÈˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆ -\Ù[‹O�™[ØÚ]KžŠJBˆˆÙ[‹O�™[ØÚ]Kžˆ OH - �H -ˆÙ[‹O›[Ù[]KœÜYY ž -NˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆ -\Ù[‹O�™[ØÚ]KžŠJBˆˆÙ[‹O�™[ØÚ]KžˆH - �H -ˆÙ[‹O›[Ù[]KœÜYY ž -NˆBˆB‚ˆ™]\›ŽÂŸB‚�›ÚY^Y\—Ú�[\ØÚXÚÊ -BžÂˆ[�Ø[™ÜÜXÚX[H ‚ˆ ËÈܘ]\È - L LŒ ŒJHš^YH›ØZ\˜Ø[˜Ù[�[˜Ý[Û‚ˆ ËÈ›ÝÈH›YÈŒˆˆÛÜšÜÈ\È[�[™Y[™ÛÛ\][H\ØX›\ÈHØ[˜Ù[][Ûˆ™]ÙY[ˆ[�[\[™È]XÚ܈ ËÈ[ˆH™]š[Ý\ÈÛÙK›Ý›YÜÈ H[™ ˆ]™HHØ[YHY™™XÝ[™[ÝÈHØ[˜Ù[][ÛˆY�\ˆ\Ý�[\]XÚÈ\Èš[š\ÚYˆYŠ -[›ØZ\˜Ø[˜Ù[ -H -›ØZ\˜Ø[˜Ù[ ŒH ‰ˆ\Ù[‹O˜[š[X][™ÊH -›ØZ\˜Ø[˜Ù[ ŒÈ ‰ˆ -Ù[‹O˜[š[[�[HOHÙ[‹Oš�[\ ˜[š[X][Û—ÚY -JJBˆˆ ËØZ\ˆÜXÚX[ ÛÜYY[™Ú[™ÙYœ›ÛH�YÝYIÜÈÛÙBˆYŠ -[]™[ O››ÜÜXÚX[]™[ O››ÜÜXÚX[OH ÊH ‰ˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÔÔPÒPS -BˆˆYŠ˜[Y[š[JÙ[‹S’WÒ•STÔPÒPS -JBˆˆ ËÈܘ]\È - L LŒ ŒJH›ÜˆØY™KYY[ˆ™]ÈÝ\ÈÚXÚÈYˆH[�]H\È›Ý[ˆHš�[\ÜXÚX[ˆ[š[X][Û‚ˆ ËÈš^\ÈH�YÈ]ÛÛœÝ[�HÛÛœÝ[Y\ÈX[܈\YˆHÜXÚX[�]Ûˆ\ÈÛÛœÝ[�H™\ÜÙY ]™[ˆY‚ˆ ËÈHÝ\œ™[�š�[\ÜXÚX[ˆ[š[X][ÛˆÞXÛH\țݚ[š\ÚYY]ˆYŠÙ[‹O˜[š[[�[HOHS’WÒ•STÔPÒPS -BˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÒ•STÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ ‰ˆÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÒ•STÔPÒPS -JBˆˆ ËÈܘ]\È - L LŒ ŒJH›ÝÈHš[™š[š]HX[ÚX]ˆÚ[Û›HÛÜšÈÚ[ˆHÛÜÝ\ÈPS Ú[›ÝÛÜšÈÚ[ˆHÛÜÝ\ÈT[ž[[Ü™BˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÒ•STÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈Ø[™ÜÜXÚX[H NˆBˆ[ÙHYŠÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÒ•STÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜÝ ‰ˆÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÒ S’WÒ•STÔPÒPS -JBˆˆYŠJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÒPSÐPÕU‘JJBˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÒ•STÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆØ[™ÜÜXÚX[H NˆBˆ[ÙHYŠ˜[Y[š[JÙ[‹S’WÒ•STÐS• -JBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STÐS•  -NˆÙ[‹O�™[ØÚ]KžHH ˆBˆB‚ˆYŠØ[™ÜÜXÚX[ -Bˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÔÔPÒPSˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘N‚ˆ ËÈܘ]\È - L LŒ ŒJHYHÜ[ÛˆÈÚ[܈›ÝH^ˆ[Ý™[Y[�ˆ ËÈܘ]\È - LŒ ŒŠHZ[›Üˆš^ÛˆH�[\ÜXÚX[ÛÙBˆYŠJÙ[‹O›[Ù[]Kš�[\ÜXÚX[ ˆ JJBˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH È ËÈÚ[[Ý™[Y[�Ú[ˆHÜXÚX[Ý\�ˆÙ[‹O�™[ØÚ]KžHH ˆB‚ˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STÔPÒPS  -NˆBˆBˆKËÙ[™Ùˆ�[\ÜXÚX[‚ˆ ËÚ�[\]XÚÜË\ÝÛˆ›Ü�Ø\™›Ü›X[ ‹‹‹�ÙHÛ‰ÝÚXÚÈ[™\™ÞHÛÜ݈[ÙHYŠ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘N‚ˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆ˜[Y[š[JÙ[‹S’WÒ•STUPÒÌŠJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STUPÒÌ‹ -NˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆ˜[Y[š[JÙ[‹S’WÒ•STUPÒÌÊJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STUPÒÌË -NˆBˆ[ÙHYŠÙ[‹Oœ�[›š[™È ‰ˆ˜[Y[š[JÙ[‹S’WÔ•S’•STUPÒÊJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÔ•S’•STUPÒË -NÈ ËÈYYÛÈ[ˆ^˜HݛۙÈ�[\]XÚÈØ[ˆ™H^XÝ]YˆBˆ[ÙHYŠÙ[‹O�™[ØÚ]KžOH  ‰ˆ˜[Y[š[JÙ[‹S’WÒ•ST“Ô•ÐT‘ -JBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•ST“Ô•ÐT‘  -NÈ ËÈYˆ[Ýš[™È[™Ù] È\È]XÚˆBˆ[ÙHYŠ˜[Y[š[JÙ[‹S’WÒ•STUPÒÊJBˆˆ[�ÜÙ]Ø[š[JÙ[‹S’WÒ•STUPÒË -NˆBˆKËÙ[™Ùˆ�[\]XÚˆB‚ˆ ʈˆ -ˆ�[\ZYÚÛÛ�›Û ˆÝÜš\Ú[™ÈYˆ�[\ˆ -ˆÙ^H\È[˜XÝ]™K‚ˆ -‹ÂˆYˆ -Ù[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖWÔÕÔ -BˆˆYˆ -J^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×Ò•ST -JBˆˆYˆ -Ù[‹O�™[ØÚ]KžHˆ -BˆˆÙ[‹O�™[ØÚ]KžH -�HRT—ÐÓÓ•“ÓÔÕÔÑ�PÕԎˆBˆBˆB‚ˆ ʈˆ -ˆ�[\ÝÜ ˆYˆ›ÝÛ[™ÈHY�Ü‚ˆ -ˆšYÚÙ^KÚ[Üš^›Û�[[ÛY[�[K‚ˆ -‹ÂˆYˆ -Ù[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖÔÕÔ -BˆˆYˆ -J^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -JJBˆˆYˆ -Ù[‹O�™[ØÚ]KžOH -BˆˆÙ[‹O�™[ØÚ]Kž -�HRT—ÐÓÓ•“ÓÔÕÔÑ�PÕԎˆBˆBˆB‚ˆ Ê‚ˆ -ˆ�[\ˆÝÜ ˆYˆ›ÝÛ[™È[ˆ\Ü‚ˆ -ˆÝÛˆÙ^KÚ[]\˜[[ÛY[�[K‚ˆ -‹ÂˆYˆ -Ù[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖ—ÔÕÔ -BˆˆYˆ -J^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓŠJJBˆˆYˆ -Ù[‹O�™[ØÚ]KžˆOH -BˆˆÙ[‹O�™[ØÚ]Kžˆ -�HRT—ÐÓÓ•“ÓÔÕÔÑ�PÕԎˆBˆBˆB‚ˆ ʈ�[\\›ˆÛÛ�›Û ˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÕT“ŠBˆˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆB‚ˆ ʈ�[\Üš^›Û�[[Ý™HÛÛ�›Û -Yˆ[™XYH[Ýš[™ÊKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖÐQ•TÕ -BˆˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆÙ[‹O�™[ØÚ]Kžˆ  -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆÙ[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O�™[ØÚ]KžˆBˆBˆˆ ʈ�[\Üš^›Û�[[Ý™HÛÛ�›Û -[žH�[\ -Kˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖÓSÕ‘JBˆÈˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆÙ[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžH \Ù[‹O�™[ØÚ]KžˆB‚ˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H ‰ˆ -\Ù[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]Kž OHÙ[‹O›[Ù[]KœÜYY žˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -H ‰ˆ -\Ù[‹O�™[ØÚ]Kž -JBˆˆÙ[‹O�™[ØÚ]KžHÙ[‹O›[Ù[]KœÜYY žˆBˆBˆˆ ʈ�[\ˆ[Ý™HÛÛ�›Û -Yˆ[™XYH[Ýš[™ÊKˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖ—ÐQ•TÕ -BˆˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆÙ[‹O�™[ØÚ]Kžˆˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆÙ[‹O�™[ØÚ]Kžˆ -JBˆˆÙ[‹O�™[ØÚ]KžˆH \Ù[‹O�™[ØÚ]KžŽÂˆBˆB‚ˆ ʈ�[\ˆ[Ý™HÛÛ�›Û -[žH�[\ -Kˆ -‹ÂˆYŠÙ[‹O›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖ—ÓSÕ‘JBˆˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆB‚ˆYŠ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆÙ[‹O�™[ØÚ]Kžˆˆ -Hˆ - -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆÙ[‹O�™[ØÚ]Kžˆ -JBˆˆÙ[‹O�™[ØÚ]KžˆH \Ù[‹O�™[ØÚ]KžŽÂˆB‚ˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰ˆ -\Ù[‹O�™[ØÚ]KžŠJBˆˆÙ[‹O�™[ØÚ]Kžˆ OH - �H -ˆÙ[‹O›[Ù[]KœÜYY ž -NˆBˆ[ÙHYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆ -\Ù[‹O�™[ØÚ]KžŠJBˆˆÙ[‹O�™[ØÚ]KžˆH - �H -ˆÙ[‹O›[Ù[]KœÜYY ž -NˆBˆB‚ˆ™]\›ŽÂŸB‚�›ÚY^Y\—ÜZ[—ØÚXÚÊ -BžÂˆYŠ^Y\—ØÚXÚ×ÜÜXÚX[ - -JBˆˆÙ[‹Oš[œZ[ˆHS—ÔRS—Ó“Ó‘NˆÙ[‹Oœš\Ú[™ÈH’TÒS‘×Ó“Ó‘NˆÙ[‹O™XÚÚ[™ÈHPÒ×Ó“Ó‘NˆÙ[‹Oš[˜˜XÚÜZ[ˆH ˆBŸB‚‹ËÈÚXÚÈš\ÙX]XÚÈ[œ]\ -Ø]XÚÂ�›ÚY^Y\—ÛYWØÚXÚÊ -BžÂˆYŠ˜[Y[š[JÙ[‹S’WÔ’TÑPUPÒÊH ‰‚ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰‚ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘UT -H ‰‚ˆ -Ù[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ  ‰ˆÝ[YHˆÙ[‹OœÝ^YÝÛ‹œš\ÙX]XÚ×ÜÝ[ -JBˆˆ^Y\–ÜÙ[‹Oœ^Y\š[™^Kœ^ZÙ^\È �H‘“Q×ÐUPÒΈYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆYŠ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆÙ[‹O™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆÙ[‹OœÝ[[YHH ˆÙ]Üš\ÙX]XÚÊÙ[‹Ù[‹O›\ÝÙ[XYÙWÝ\K -NˆBŸB‚�›ÚY^Y\—ØÚ\™ÙWØÚXÚÊ -BžÂˆYŠJ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×Ò•ST -H ‰‚ˆ -^Y\–ÜÙ[‹Oœ^Y\š[™^KšÙ^\È ˆ“Q×ÔÔPÒPS -JJBˆˆÙ[‹O�ZÙXXÝ[ÛˆH•SˆÙ[‹O˜Ú\™Ú[™ÈH ˆÙ]ÚYJÙ[ŠNˆBŸB‚‹ËÈXZÙHH�[˜Ý[ÛˆÛÈ[™[ZY\ÈØ[ˆ\ÙB‹ËÈUˆ�[\XÚÈ\ÈH[\ܘ\žHš^›Üˆ�[\Ø[˜Ù[š[�ÚXÚרÛÜÝ[Ý™J[�Ë[�œË[��[\XÚÊBžÂˆYŠ - -œÈOH H ‰ˆ]™[ O››ÜÜXÚX[ ŠH -œÈOH  ‰ˆ]™[ O››ÜÜXÚX[OH -H -œÈOH  ‰ˆ]™[ O››ÜÜXÚX[OH ÊJH ‰‚ˆ -ÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÒ ÊHˆÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT ÊJH -BˆˆYŠZ�[\XÚÊBˆˆÙ[‹O�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆBˆ ËÈܘ]\È - L LŒ ŒJH›ÝÈHš[™š[š]HX[ÚX]ˆY™™XÝÈ^Y\œÈÛ›K›Ý[™[ZY\È܈œˆ ËÈ[™›ÝÈHš[™š[š]HX[ÚX]ˆÚ[Û›HÛÜšÈÚ[ˆHÛÜÝ\ÈPS Ú[›ÝÛÜšÈÚ[ˆHÛÜÝ\ÈT[ž[[Ü™BˆYŠÙ[‹O›[Ù[]K�\H ˆTWÔVQTŠBˆˆYŠ[›ØÛÜÝ -BˆˆYŠJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÒPSÐPÕU‘JJBˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT ÊJBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜ݈Bˆ[ÙBˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆ[ÙBˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT ÊJBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆBˆBˆ[ÙBˆˆYŠ[›ØÛÜÝ -BˆˆYŠÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜÝ -BˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT ÊJBˆˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜ݈Bˆ[ÙBˆˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–Ü×KO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆBˆBˆB‚‚B\Ù[‹Oœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆÙ]Ø]XÚÚ[™ÊÙ[ŠNˆÙ[‹Oš[œZ[ˆHS—ÔRS—Ó“Ó‘NˆÙ[‹Oœš\Ú[™ÈH’TÒS‘×Ó“Ó‘NˆÙ[‹Oš[˜˜XÚÜZ[ˆH ˆY[\Ù] -Ù[‹O˜ÛÛX›ÜÝ\  Ú^™[ÙŠ -œÙ[‹O˜ÛÛX›ÜÝ\ -H -ˆ JNˆ[�Ý[›[šÊÙ[ŠNˆ[�ÜÙ]Ø[š[JÙ[‹Ë -Nˆ™]\›ˆ NˆBˆ™]\›ˆ ŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚Šˆ™XÛÜ™HÝ\�[™È[YH›Üˆ]™\žH™]ÛH™\ÜÙYŠˆÛÛ[X[™[œ]›YË‚Š‚Šˆ\ÚXØ[[™\™XÝ[Û‹\™[]]™H›YÜÈÚ\™HHØ[YBŠˆ � Xš]˜[Y\ÜXÙKˆ\Ú[™ÈH›YÉÜÈš][™^›ÝšY\ŠˆÛ[Z[™ÈÚ]Ý]H\˜[[ÛÛ[X[™ Z\ÝÜžHš[™Ë‚Š‹ÂœÝ]XÈ›ÚYÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý\]Jˆ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆÛÛœÝÙ^WÛX\Ú×Ý™\ÜÙYÙ›YÜˈÛÛœÝZ[� �Ý]™[�Ý[YBŠHˆÙ^WÛX\Ú×ÝØØ[—ÛX\ÚÈH™\ÜÙYÙ›YÜ΂ˆÚ[JØØ[—ÛX\ÚÊHˆÛÛœÝZ[� �Ý[œ]Ú[™^Bˆš]X\ÚÍ�ÙÙ]ÛÝÙ\ÝÚ[™^ -ØØ[—ÛX\ÚÊN‚ˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÈBˆš]X\ÚÍ�Ùœ›ÛWÚ[™^ -[œ]Ú[™^ -N‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YVˆ[œ]Ú[™^ˆHH]™[�Ý[YN‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�ݘ[YBˆ[œ]Ù›Y΂ˆ Ê‚ˆ -ˆH™]È™\ÜÈ™YÚ[œÈH™]ÈÛÛ�[�[Ý\ÈÛ ˆÛX\‚ˆ -ˆH]]ÛX]XË]šYÙÙ\ˆ™XÛÜ™ÛÈ[YH™\›ÈØ[‚ˆ -ˆšYÙÙ\ˆYØZ[ˆ›ÜˆH™]ÈÛ ‚ˆ -‹ÂˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ݘ[Y �Bˆš[œ]Ù›Y΂ˆØØ[—ÛX\ÚÈ �Hš[œ]Ù›YΈBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚ŠˆÛÛXÝ]]ÛX]XÈ[ Z[œ]YÙ\ÈÚXÚ™XXÚZ\‚ŠˆÛÛ™šYÝ\™Y™\ÚÛÛˆ\ÈÙÚXØ[XÚË‚Š‚Šˆ™\ÚÛÈÛÛYHœ›ÛHHXÝ[™È[�]IÜÈÛÛ™šYÝ\˜X›BŠˆÜXÚX[ÛÛ[X[™ˈH\‹Z[œ][Y\Ý[\™]™[�ÈBŠˆÙXÛÛ™[œ]™Yœ™\Ú\š[™ÈHØ[YHXÚÈœ›ÛH[Z][™ÂŠˆHØ[YHYÙHYØZ[‹ˆY™™\™[�™\ÚÛț܈HØ[YBŠˆ[œ]™[XZ[ˆ[™\[™[�™XØ]\ÙHXXÚ\È™XXÚYÛˆBŠˆY™™\™[�ÙÚXØ[XÚÈ\š[™ÈÛ™HÛÛ�[�[Ý\ÈÛ ‚Š‹ÂœÝ]XÈÙ^WÛX\Ú×ÝÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ØÛÛXÝ -ˆ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆÛÛœÝÙ^WÛX\Ú×Ý[Ù›YÜˈÛÛœÝZ[� �Ý]™[�Ý[YBŠHˆÛÛœÝ[�]JˆXÝ[™×Ù[�]N‚ˆÙ^WÛX\Ú×ÝÛÛXÝYÙ›YÜÈH ‚ˆ[�ÜXÚX[Ú[™^‚ˆYŠXXÝ[™×Ü^Y\ˆXXÝ[™×Ü^Y\‹O™[� -Hˆ™]\›ˆ ˆB‚ˆXÝ[™×Ù[�]HHXÝ[™×Ü^Y\‹O™[�‚ˆ›ÜŠÜXÚX[Ú[™^H ˆÜXÚX[Ú[™^XÝ[™×Ù[�]KO›[Ù[]KœÜXÚX[×ÛØYYˆÜXÚX[Ú[™^ -ÊÊH‚ˆÛÛœÝרÛÛJˆÜXÚX[ØÛÛ[X[™Bˆ ˜XÝ[™×Ù[�]KO›[Ù[]KœÜXÚX[ÜÜXÚX[Ú[™^N‚ˆ[�Ý\Ú[™^‚ˆYŠÜXÚX[ØÛÛ[X[™ OœÝ\ÈH ˆÜXÚX[ØÛÛ[X[™ OœÝ\ȈPVÔÔPÒPSÒS”UÊHˆÛÛ�[�YNˆB‚ˆ›ÜŠÝ\Ú[™^H ˆÝ\Ú[™^ÜXÚX[ØÛÛ[X[™ OœÝ\ΈÝ\Ú[™^ -ÊÊH‚ˆÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\ -ˆ[œ]ÜÝ\Bˆ œÜXÚX[ØÛÛ[X[™ Oš[œ]ÜÝ\Ú[™^N‚ˆÙ^WÛX\Ú×ÝØØ[—ÛX\ÚÈBˆ[œ]ÜÝ\ OšÛÝšYÙÙ\ˆ ˆ[Ù›YÜ΂ˆÚ[JØØ[—ÛX\ÚÊHˆÛÛœÝZ[� �Ý[œ]Ú[™^Bˆš]X\ÚÍ�ÙÙ]ÛÝÙ\ÝÚ[™^ -ØØ[—ÛX\ÚÊN‚ˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÈBˆš]X\ÚÍ�Ùœ›ÛWÚ[™^ -[œ]Ú[™^ -N‚ˆÛÛœÝZ[� �ÝÛÜÝ\�Ý[YHBˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YVˆ[œ]Ú[™^ˆN‚ˆÛۜݛÛÛ[™XYWÙ[Z]YÝ\×ÝXÚÈBˆ -XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ݘ[Yˆ ˆ[œ]Ù›YÊBˆ ‰ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—Ý[YVˆ[œ]Ú[™^ˆHOH]™[�Ý[YN‚ˆYŠ -XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�ݘ[Yˆ ˆ[œ]Ù›YÊBˆ ‰ˆÛÜÝ\�Ý[YHH]™[�Ý[YBˆ ‰ˆ]™[�Ý[YH HÛÜÝ\�Ý[YBˆOH[œ]ÜÝ\ OšÛÝ[YBˆ ‰ˆX[™XYWÙ[Z]YÝ\×ÝXÚÊH‚ˆÛÛXÝYÙ›YÜÈH[œ]Ù›YΈB‚ˆØØ[—ÛX\ÚÈ �Hš[œ]Ù›YΈBˆBˆB‚ˆ Ê‚ˆ -ˆ™XÛÜ™[Z]Y›YÜÈÛ›HY�\ˆØØ[›š[™È]™\žBˆ -ˆÛÛ[X[™ ˆÛÛ[X[™ÈÚ\š[™ÈHØ[YH[œ][™ˆ -ˆ™\ÚÛ[X™\˜][HÚ\™HÛ™H\ÝÜžHYÙK‚ˆ -‹ÂˆˆÙ^WÛX\Ú×ÝØØ[—ÛX\ÚÈHÛÛXÝYÙ›YÜ΂ˆÚ[JØØ[—ÛX\ÚÊHˆÛÛœÝZ[� �Ý[œ]Ú[™^Bˆš]X\ÚÍ�ÙÙ]ÛÝÙ\ÝÚ[™^ -ØØ[—ÛX\ÚÊN‚ˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÈBˆš]X\ÚÍ�Ùœ›ÛWÚ[™^ -[œ]Ú[™^ -N‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—Ý[YVˆ[œ]Ú[™^ˆHH]™[�Ý[YN‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ݘ[YBˆ[œ]Ù›Y΂ˆØØ[—ÛX\ÚÈ �Hš[œ]Ù›YΈBˆB‚ˆ™]\›ˆÛÛXÝYÙ›YÜΟB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚Šˆ\[™Û™H]™[�ÈH^Y\‰ÜÈÛÛ[X[™\ÝÜžK‚Š‹ÂœÝ]XÈ›ÚYÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ\Ú -×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[� -HˆˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžVØXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^HH -š[œ]Ù]™[�‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^H -XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^ -È JH ˆÔPÒPSÒS”UÒS‘VÓPTÒ΂ˆYŠXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�PVÔÔPÒPSÒS”UÊHˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[� -ÊΈBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚ŠˆÛÛœÝ[YHH™]Ù\Ý™\ÜÈYÙHœ›ÛHH^Y\‰ÜÈÛÛ[X[™Šˆ\ÝÜžK‚Š‚ŠˆÙ]™\˜[\™ XÛÙY[Ý™[Y[�ÛÛ[X[™ÈÛÛœÝ[YHZ\‚ŠˆšYÙÙ\š[™È™\ÜˈHÛÛXš[™Y]™[�X^H[ÛÈÛÛ�Z[ˆBŠˆ™[X\ÙH܈]]ÛX]XÈÛYÙKÛÈÛ›H™[[Ý™HBŠˆÛÛ\]H[�žHÚ[ˆ›ÈÝ\ˆYÙH™[XZ[œË‚Š‹ÂœÝ]XÈ›ÚYÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\Üʈ×Ü^Y\ŠˆXÝ[™×Ü^Y\‚ŠHˆרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆZ[� �Ý[œ]Ú[™^‚ˆYŠXXÝ[™×Ü^Y\ˆXXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[� -Hˆ™]\›ŽÂˆB‚ˆ[œ]Ú[™^Bˆ -XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^ H JBˆ ˆÔPÒPSÒS”UÒS‘VÓPTÒ΂ˆ[œ]Ù]™[�Bˆ ˜XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžVˆ[œ]Ú[™^ˆN‚ˆ[œ]Ù]™[� Oœ™\ÜÈH ˆ[œ]Ù]™[� Oœ™\ÜרÚÜ™H ‚ˆYŠ[œ]Ù]™[� OšÛ[œ]Ù]™[� Oœ™[X\ÙJHˆ™]\›ŽÂˆB‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^H[œ]Ú[™^‚ˆY[\Ù] -[œ]Ù]™[�  Ú^™[ÙŠ -š[œ]Ù]™[� -JN‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[� KNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚ŠˆÛÛœÝ[YHÙ[XÝY]]ÛX]XÈ[ Z[œ]›YÜÈœ›ÛHBŠˆ]™[�ÚXÚšYÙÙ\™YHÛÛ™šYÝ\˜X›HÛÛ[X[™ ‚Š‚ŠˆÝ\ˆ›YÜÈ™[XZ[ˆ]˜Z[X›HÚ[ˆHØ[YH\ÝÜžBŠˆ[�žH[ÛÈÛÛ�Z[œÈH™\ÜË™[X\ÙK܈[›Ý\ˆ[ŠˆYÙKˆ[\H›Û‹[]\Ý[�šY\ÈÝ^H[ˆXÙHÛÈš[™ÂŠˆÜ™\ˆ[™ÝÜ™Y]™[�YÙ\ÈțݙYYÛÛ\XÝ[Û‹‚Š‹ÂœÝ]XÈ›ÚYÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÚÛÝšYÙÙ\Šˆ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆÛÛœÝZ[� �Ý]™[�ØYÙKˆÛÛœÝÙ^WÛX\Ú×ÝšYÙÙ\—Ù›YÜŠHˆרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆZ[� �Ý[œ]Ú[™^‚ˆYŠXXÝ[™×Ü^Y\‚ˆ]šYÙÙ\—Ù›Y܈]™[�ØYÙH�HXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[� -Hˆ™]\›ŽÂˆB‚ˆ[œ]Ú[™^Bˆ -XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^ H]™[�ØYÙH H JBˆ ˆÔPÒPSÒS”UÒS‘VÓPTÒ΂ˆ[œ]Ù]™[�Bˆ ˜XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžVÚ[œ]Ú[™^N‚ˆ[œ]Ù]™[� OšÛ �H�šYÙÙ\—Ù›YÜ΂ˆYŠ[œ]Ù]™[� Oœ™\܈[œ]Ù]™[� OšÛˆ[œ]Ù]™[� Oœ™[X\ÙBˆ]™[�ØYÙJHˆ™]\›ŽÂˆB‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^H[œ]Ú[™^‚ˆY[\Ù] -[œ]Ù]™[�  Ú^™[ÙŠ -š[œ]Ù]™[� -JN‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[� KNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚Šˆ™XYH™XÙY[™È˜[Y]™[�œ›ÛHH^Y\‰ÜŠˆÛÛ[X[™\ÝÜžK‚Š‹ÂœÝ]XÈÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\ʈÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆZ[� �Ý -ˆ\ÝÜžWÚ[™^ ˆZ[� �Ý -ˆ™[XZ[š[™×Ù]™[�ŠHˆYŠJœ™[XZ[š[™×Ù]™[�ÊHˆ™]\›ˆ•SˆB‚ˆ -š\ÝÜžWÚ[™^Bˆ - -š\ÝÜžWÚ[™^ H JH ˆÔPÒPSÒS”UÒS‘VÓPTÒ΂ˆ - -œ™[XZ[š[™×Ù]™[�ÊKKN‚ˆ™]\›ˆ ˜XÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžVˆ -š\ÝÜžWÚ[™^ˆNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚Šˆ™XYH™XÙY[™È™\ÜÈ]™[�œ›ÛHH^Y\‰ÜŠˆÛÛ[X[™\ÝÜžK‚Š‚Šˆ›Û‹\™\ÜÈ[�šY\È\™HÚÚ\YÛÈ™[X\ÙH[™[YYŠˆÛ]™[�ÈØ[ˆ™HYYÚ]Ý]Ú[™Ú[™ÈYØXÞBŠˆ\™ XÛÙYÛÛ[X[™™Z]š[Ü‹‚Š‹ÂœÝ]XÈÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×Ü™\ÜʈÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆZ[� �Ý -ˆ\ÝÜžWÚ[™^ ˆZ[� �Ý -ˆ™[XZ[š[™×Ù]™[�ŠHˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆÚ[J -[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\ʈXÝ[™×Ü^Y\‹ˆ\ÝÜžWÚ[™^ ˆ™[XZ[š[™×Ù]™[�ˆ -JJH‚ˆYŠ[œ]Ù]™[� Oœ™\ÜÊHˆ™]\›ˆ[œ]Ù]™[�ˆBˆB‚ˆ™]\›ˆ•SŸB‚‹Ê‚ŠˆÚXÚÈÚ]\ˆH^Y\‰ÜÈ[ÜÝ™XÙ[�™\ÜŠˆ]™[�ÈØ]\ÙžHH\™ XÛÙYÛÛ[X[™Ù\]Y[˜ÙK‚Š‚ŠˆXXÚÙ\]Y[˜ÙH[�žH\ÈHš]X\ÚÈÙˆ™\]Z\™YŠˆ[œ]ˈH™XÛÜ™Y™\ÜÈX^HÛÛ�Z[ˆY][Û˜[Šˆ›YÜÈ[™Ý[X]Ú ‚Š‚Šˆ™[X\ÙH[™[YYÛ]™[�È\™H[X™\˜][BŠˆYۛܙYÛÈ^\Ý[™È\™ XÛÙYÛÛ[X[™È™]Z[‚ŠˆZ\ˆÜÚ]]™KYYÙH™Z]š[Ü‹‚Š‹ÂœÝ]XÈ›ÛÛX]ÚØÛÛX›ÊÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙV×KÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ÛÛœÝZ[� �Ý[™Ý -HˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆZ[� �Ý\ÝÜžWÚ[™^ˆZ[� �Ý™]Ù\—Ù]™[�Ý[YNˆZ[� �Ý™[XZ[š[™×Ù]™[�ΈZ[� �ÝÙ\]Y[˜ÙWÚ[™^‚ˆYŠ\Ù\]Y[˜ÙBˆXXÝ[™×Ü^Y\‚ˆ[[™Ýˆ[™ÝˆPVÔÔPÒPSÒS”UÊHˆ™]\›ˆ˜[ÙNˆB‚ˆ\ÝÜžWÚ[™^HXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^ˆ™]Ù\—Ù]™[�Ý[YHHÝ[YNˆ™[XZ[š[™×Ù]™[�ÈHXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�‚ˆ›ÜŠÙ\]Y[˜ÙWÚ[™^H[™ÝˆÙ\]Y[˜ÙWÚ[™^ˆ ˆÙ\]Y[˜ÙWÚ[™^ KJH‚ˆÛÛœÝÙ^WÛX\Ú×Ý™\]Z\™YÜ™\ÜÈBˆÙ\]Y[˜ÙVÜÙ\]Y[˜ÙWÚ[™^ H WN‚ˆ[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×Ü™\ÜʈXÝ[™×Ü^Y\‹ˆ š\ÝÜžWÚ[™^ ˆ œ™[XZ[š[™×Ù]™[�ˆ -N‚ˆYŠZ[œ]Ù]™[�ˆ -[œ]Ù]™[� Oœ™\ÜÈ ˆ™\]Z\™YÜ™\ÜÊBˆOH™\]Z\™YÜ™\܈™]Ù\—Ù]™[�Ý[YH H[œ]Ù]™[� O�[YBˆˆÛؘ[ØÛÛ™šY˘ÛÛ[X[™Ý[YJHˆ™]\›ˆ˜[ÙNˆB‚ˆ™]Ù\—Ù]™[�Ý[YHH[œ]Ù]™[� O�[YNˆB‚ˆ™]\›ˆ�YNŸB‚‹Ê‚Šˆ™]\›ˆHܘXÙH[YH\ÙYžHÛ™HÛÛ™šYÝ\˜X›HÛÛ[X[™ŠˆÙ\]Y[˜ÙKˆÛÛ[X[™ÈÚ]Ý][ˆ^XÚ]Ý™\œšYH[š\š]ŠˆH[™Ú[™K]ÚYHÙ][™È]X]Ú[YK‚Š‹ÂœÝ]XÈZ[� �ÝÛÛ[X[™ÜÙ\]Y[˜ÙWÙܘXÙWÝ[YWÙÙ] -ˆÛÛœÝרÛÛJˆÜXÚX[ØÛÛ[X[™ŠHˆ\ÜÙ\� -ÜXÚX[ØÛÛ[X[™ -N‚ˆ™]\›ˆÜXÚX[ØÛÛ[X[™ OœÙ\]Y[˜ÙWÙܘXÙWÝ[YWÛÝ™\œšYBˆÈÜXÚX[ØÛÛ[X[™ OœÙ\]Y[˜ÙWÙܘXÙWÝ[YBˆˆÛؘ[ØÛÛ™šY˘ÛÛ[X[™Ý[YNŸB‚‹Ê‚Šˆ™]\›ˆHÛ™Ù\ÝܘXÙH[YH™YYYžHHXÝ[™ÂŠˆ[�]IÜÈÛÛ™šYÝ\˜X›H[™\™ XÛÙYÛÛ[X[™Ë‚Š‚Šˆ[œ]\ÝÜžH\ÈÚ\™YžH]™\žHÛÛ[X[™ ˆ™]Z[š[™È]Šˆ›ÜˆHÛ™Ù\ÝÛÛ™šYÝ\™YܘXÙH™]™[�ÈHÛÛ[X[™ŠˆÚ]HÛ™Ù\ˆØØ[Ý™\œšYHœ›ÛHÜÚ[™È]ÈX\›Y\‚ŠˆÝ\ÈÈHÛؘ[\ÝÜžH[Y[Ý] ‚Š‹ÂœÝ]XÈZ[� �ÝÛÛ[X[™Ú[œ]Ú\ÝÜžWÙܘXÙWÝ[YWÙÙ] -ˆÛÛœÝ[�]JˆXÝ[™×Ù[�]BŠHˆZ[� �Ý\ÝÜžWÙܘXÙWÝ[YHHÛؘ[ØÛÛ™šY˘ÛÛ[X[™Ý[YN‚ˆ[�ÜXÚX[Ú[™^‚ˆYŠXXÝ[™×Ù[�]JHˆ™]\›ˆ\ÝÜžWÙܘXÙWÝ[YNˆB‚ˆ›ÜŠÜXÚX[Ú[™^H ˆÜXÚX[Ú[™^XÝ[™×Ù[�]KO›[Ù[]KœÜXÚX[×ÛØYYˆÜXÚX[Ú[™^ -ÊÊH‚ˆÛÛœÝרÛÛJˆÜXÚX[ØÛÛ[X[™Bˆ ˜XÝ[™×Ù[�]KO›[Ù[]KœÜXÚX[ÜÜXÚX[Ú[™^N‚ˆÛÛœÝZ[� �ÝÙ\]Y[˜ÙWÙܘXÙWÝ[YHBˆÛÛ[X[™ÜÙ\]Y[˜ÙWÙܘXÙWÝ[YWÙÙ] -ÜXÚX[ØÛÛ[X[™ -N‚ˆYŠÙ\]Y[˜ÙWÙܘXÙWÝ[YHˆ\ÝÜžWÙܘXÙWÝ[YJHˆ\ÝÜžWÙܘXÙWÝ[YHHÙ\]Y[˜ÙWÙܘXÙWÝ[YNˆBˆB‚ˆ™]\›ˆ\ÝÜžWÙܘXÙWÝ[YNŸB‚‹Ê‚Šˆ™]\›ˆHXœÛÛ]HÙÚXØ[XÚÈÚ[ˆÚ\™YÛÛ[X[™Šˆ\ÝÜžH^\™\ˈØ]\˜][Ûˆ™\Ù\�™\È^™[Y[H\™ÙBŠˆÜ™X]Ü‹YYš[™YܘXÙH˜[Y\ÈÚ]Ý]ܘ\[™ÈBŠˆ^\˜][Ûˆ[YH\›Ý[™ÈH™YÚ[›š[™ÈÙˆHÛØÚË‚Š‹ÂœÝ]XÈZ[� �ÝÛÛ[X[™Ú[œ]Ú\ÝÜžWÙ^\˜][Û—Ý[YWÙÙ] -ˆÛÛœÝ[�]JˆXÝ[™×Ù[�]KˆÛÛœÝZ[� �Ý]™[�Ý[YBŠHˆÛÛœÝZ[� �Ý\ÝÜžWÙܘXÙWÝ[YHBˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÙܘXÙWÝ[YWÙÙ] -XÝ[™×Ù[�]JN‚ˆYŠ\ÝÜžWÙܘXÙWÝ[YHˆRS• �ÓPV H]™[�Ý[YJHˆ™]\›ˆRS• �ÓPVˆB‚ˆ™]\›ˆ]™[�Ý[YH -È\ÝÜžWÙܘXÙWÝ[YNŸB‚‹Ê‚Šˆ™]\›ˆÚ]\ˆ[ˆ[œ]]™[�Ø]\ÙšY\ÈHÜÚ]]™BŠˆYÙH™\]Z\™[Y[�›ÜˆÛ™HÛÛ™šYÝ\˜X›HÛÛ[X[™Ý\ ‚Š‚ŠˆÚ[™ÛKZÙ^HÝ\È™]Z[ˆ^XÝYØXÞH™Z]š[܈[™\ÙBŠˆÛ›HH\ÚXØ[™\ÜÈYÙKˆ][KZÙ^HÝ\È\ÙHBŠˆÚܙۘ\ÚÝ[™Z\ˆÛÛ™šYÝ\™YÚÜ™Ý[YKˆ™\›ÂŠˆ™\]Z\™\È]™\žHY[X™\ˆÈ™YÚ[ˆÛˆHØ[YHÙÚXØ[XÚË‚Šˆ]X\ÝÛ™H™\]Z\™YÙ^H]\Ý™H\ÚXØ[H™\ÜÙY[‚ŠˆHšYÙÙ\š[™È]™[� ™]™[�[™È[ˆ[œ™[]Y\™Ù^BŠˆœ›ÛH™]šYÙÙ\š[™ÈHÚÜ™ÚXÚY\™[H™[XZ[œÈ[ ‚Š‹ÂœÝ]XÈ›ÛÛÛÛ[X[™Ú[œ]Ù]™[�ÛX]Ú\×Ü™\ÜʈÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\ -ˆ[œ]ÜÝ\ ˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[� ˆÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‚ŠHˆÛÛœÝÙ^WÛX\Ú×Ý™\]Z\™YÜ™\ÜÈH[œ]ÜÝ\ Oœ™\Ü΂ˆÙ^WÛX\Ú×ÝØØ[—ÛX\Ú΂ˆYŠ\™\]Z\™YÜ™\ÜÊHˆ™]\›ˆ�YNˆB‚ˆ Ê‚ˆ -ˆHX\ÚÈÚ]Û™HXÝ]™Hš]\È›ÈÙXÛÛ™š]Y�\‚ˆ -ˆ™[[Ýš[™È]ÈÝÙ\Ýš] ‚ˆ -‹ÂˆYŠJ™\]Z\™YÜ™\ÜÈ ˆ -™\]Z\™YÜ™\ÜÈ H JJJHˆ™]\›ˆ -[œ]Ù]™[� Oœ™\ÜÈ ˆ™\]Z\™YÜ™\ÜÊBˆOH™\]Z\™YÜ™\ÜΈB‚ˆYŠJ[œ]Ù]™[� Oœ™\ÜÈ ˆ™\]Z\™YÜ™\ÜÊBˆ -[œ]Ù]™[� Oœ™\ÜרÚÜ™ ˆ™\]Z\™YÜ™\ÜÊBˆOH™\]Z\™YÜ™\ÜÊHˆ™]\›ˆ˜[ÙNˆB‚ˆØØ[—ÛX\ÚÈH™\]Z\™YÜ™\Ü΂ˆÚ[JØØ[—ÛX\ÚÊHˆÛÛœÝZ[� �Ý[œ]Ú[™^Bˆš]X\ÚÍ�ÙÙ]ÛÝÙ\ÝÚ[™^ -ØØ[—ÛX\ÚÊN‚ˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÈBˆš]X\ÚÍ�Ùœ›ÛWÚ[™^ -[œ]Ú[™^ -N‚ˆÛÛœÝZ[� �Ý™\Ü×Ý[YHBˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YVˆ[œ]Ú[™^ˆN‚ˆYŠJXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�ݘ[Yˆ ˆ[œ]Ù›YÊBˆ™\Ü×Ý[YHˆ[œ]Ù]™[� O�[YBˆ[œ]Ù]™[� O�[YH H™\Ü×Ý[YBˆˆ[œ]ÜÝ\ O˜ÚÜ™Ý[YJHˆ™]\›ˆ˜[ÙNˆB‚ˆØØ[—ÛX\ÚÈ �Hš[œ]Ù›YΈB‚ˆ™]\›ˆ�YNŸB‚‹Ê‚Šˆ™]\›ˆÚ]\ˆ[ˆ[œ]]™[�Ø]\ÙšY\È]™\žH\ÜÚ]™BŠˆ[™]]ÛX]XÈ[™\]Z\™[Y[�›ÜˆÛ™HÛÛ[X[™Ý\ ‚Š‹ÂœÝ]XÈ›ÛÛÛÛ[X[™Ú[œ]Ù]™[�ÛX]Ú\×ÚÛ -ˆÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\ -ˆ[œ]ÜÝ\ ˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[� ˆÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‚ŠHˆÛÛœÝÙ^WÛX\Ú×Ý™\]Z\™YÚÛÙ›YÜÈBˆ[œ]ÜÝ\ OšÛ[œ]ÜÝ\ OšÛÝšYÙÙ\ŽÂ‚ˆÙ^WÛX\Ú×ÝØØ[—ÛX\Ú΂ˆYŠ\™\]Z\™YÚÛÙ›YÜÊHˆ™]\›ˆ�YNˆB‚ˆYŠ -[œ]Ù]™[� Oš[ ˆ™\]Z\™YÚÛÙ›YÜÊBˆOH™\]Z\™YÚÛÙ›YÜÊHˆ™]\›ˆ˜[ÙNˆB‚ˆØØ[—ÛX\ÚÈH™\]Z\™YÚÛÙ›YÜ΂ˆÚ[JØØ[—ÛX\ÚÊHˆÛÛœÝZ[� �Ý[œ]Ú[™^Bˆš]X\ÚÍ�ÙÙ]ÛÝÙ\ÝÚ[™^ -ØØ[—ÛX\ÚÊN‚ˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÈBˆš]X\ÚÍ�Ùœ›ÛWÚ[™^ -[œ]Ú[™^ -N‚ˆÛÛœÝZ[� �ÝÛÜÝ\�Ý[YHBˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YVˆ[œ]Ú[™^ˆN‚ˆYŠJXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�ݘ[Yˆ ˆ[œ]Ù›YÊBˆÛÜÝ\�Ý[YHˆ[œ]Ù]™[� O�[YJHˆ™]\›ˆ˜[ÙNˆB‚ˆˆÛÛœÝZ[� �Ý[Ý[YHBˆ[œ]Ù]™[� O�[YH HÛÜÝ\�Ý[YN‚ˆ Ê‚ˆ -ˆ\ÜÚ]™HÛÈXØÙ\H[˜Û\Ú]™HZ[š[][Bˆ -ˆ›ÝYÚHÜ[Û˜[[˜Û\Ú]™HX^[][KˆBˆ -ˆ™\›ÈX^[][HX]™\ÈH\\ˆ›Ý[™Ü[‹‚ˆ -ˆ]]ÛX]XÈÛÈX]ÚÛ›HZ\ˆ^X݈ -ˆZ[š[][H™\ÚÛYÙK™]™[�[™ÈH]\‚ˆ -ˆ™\Úۛ܈HØ[YHÙ^Hœ›ÛH™]šYÙÙ\š[™Âˆ -ˆ[ˆX\›Y\ˆ]]ÛX]XÈÛÛ[X[™ ‚ˆ -‹ÂˆYŠ - -[œ]ÜÝ\ OšÛ ˆ[œ]Ù›YÊBˆ ‰ˆ -[Ý[YH[œ]ÜÝ\ OšÛÝ[YBˆ -[œ]ÜÝ\ OšÛÝ[YWÛX^[][Bˆ ‰ˆ[Ý[YBˆˆ[œ]ÜÝ\ OšÛÝ[YWÛX^[][JJJBˆ - -[œ]ÜÝ\ OšÛÝšYÙÙ\ˆ ˆ[œ]Ù›YÊBˆ ‰ˆ[Ý[YHOH[œ]ÜÝ\ OšÛÝ[YJJHˆ™]\›ˆ˜[ÙNˆBˆB‚ˆØØ[—ÛX\ÚÈ �Hš[œ]Ù›YΈB‚ˆ™]\›ˆ�YNŸB‚‹Ê‚Šˆ™XYH™XÙY[™È]™[�ÚÜÙHYÙH\H\È™[]˜[�ŠˆÈHÛÛ™šYÝ\˜X›HÛÛ[X[™Ý\ ‚Š‚Šˆ™[X\ÙK[Û›H]™[�ÈÈ›Ý\Ý\˜ˆ™\ÜË[Û›HÝ\ËŠˆ[™™\ÜË[Û›H]™[�ÈÈ›Ý\Ý\˜ˆ™[X\ÙK[Û›BŠˆÝ\ˈ[ˆ]™[�ÙˆH™\]Z\™Y\HÚ]HܛۙÈÙ^BŠˆ\È™]\›™YÛÈ]ÛÜœ™XÝH[�\œ�\ÈHÙ\]Y[˜ÙK‚Š‹ÂœÝ]XÈÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×ٛܗÜÝ\ -ˆÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\ -ˆ[œ]ÜÝ\ ˆZ[� �Ý -ˆ\ÝÜžWÚ[™^ ˆZ[� �Ý -ˆ™[XZ[š[™×Ù]™[�ŠHˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆÚ[J -[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\ʈXÝ[™×Ü^Y\‹ˆ\ÝÜžWÚ[™^ ˆ™[XZ[š[™×Ù]™[�ˆ -JJH‚ˆYŠ -[œ]ÜÝ\ Oœ™\ÜÈ ‰ˆ[œ]Ù]™[� Oœ™\ÜÊBˆ -[œ]ÜÝ\ Oœ™[X\ÙH ‰ˆ[œ]Ù]™[� Oœ™[X\ÙJBˆ -[œ]ÜÝ\ OšÛÝšYÙÙ\ˆ ‰ˆ[œ]Ù]™[� OšÛ -JHˆ™]\›ˆ[œ]Ù]™[�ˆBˆB‚ˆ™]\›ˆ•SŸB‚‹Ê‚ŠˆÚXÚÈÚ]\ˆH^Y\‰ÜÈ[ÜÝ™XÙ[�[œ]]™[�ŠˆØ]\ÙžHHÛÛ™šYÝ\˜X›HÜXÚX[ÛÛ[X[™ ‚Š‚Šˆ™\ÜË™[X\ÙK[™]]ÛX]XÈÛX\ÚÜÈ\™HYÙBŠˆ™\]Z\™[Y[�ˈ\ÜÚ]™HÛ\ÈHÝ]H[™Z[š[][BŠˆ\˜][Ûˆ™\]Z\™[Y[�]˜[X]Y]HšYÙÙ\š[™È]™[� ‚Š‹ÂœÝ]XÈ›ÛÛX]ÚÜÜXÚX[ØÛÛ[X[™ -ˆÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\Ù\]Y[˜ÙV×KˆÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\‹ˆÛÛœÝZ[� �Ý[™Ý ˆÛÛœÝZ[� �ÝÙ\]Y[˜ÙWÙܘXÙWÝ[YKˆZ[� �Ý -ˆX]ÚÝ[YKˆZ[� �Ý -ˆX]ÚØYÙBŠHˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ[œ]Ù]™[�‚ˆZ[� �Ý\ÝÜžWÚ[™^ˆZ[� �Ý™]Ù\—Ù]™[�Ý[YNˆZ[� �Ý™[XZ[š[™×Ù]™[�ΈZ[� �ÝÙ\]Y[˜ÙWÚ[™^‚ˆYŠ\Ù\]Y[˜ÙBˆXXÝ[™×Ü^Y\‚ˆ[X]ÚÝ[YBˆ[X]ÚØYÙBˆ[[™Ýˆ[™ÝˆPVÔÔPÒPSÒS”UÊHˆ™]\›ˆ˜[ÙNˆB‚ˆ -›X]ÚÝ[YHH ˆ -›X]ÚØYÙHHRS• �ÓPV‚ˆ\ÝÜžWÚ[™^HXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^ˆ™]Ù\—Ù]™[�Ý[YHHÝ[YNˆ™[XZ[š[™×Ù]™[�ÈHXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�‚ˆ›ÜŠÙ\]Y[˜ÙWÚ[™^H[™ÝˆÙ\]Y[˜ÙWÚ[™^ˆ ˆÙ\]Y[˜ÙWÚ[™^ KJH‚ˆÛÛœÝרÛÛ[X[™Ú[œ]ÜÝ\ -ˆ[œ]ÜÝ\Bˆ œÙ\]Y[˜ÙVÜÙ\]Y[˜ÙWÚ[™^ H WN‚ˆ[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×ٛܗÜÝ\ -ˆXÝ[™×Ü^Y\‹ˆ[œ]ÜÝ\ ˆ š\ÝÜžWÚ[™^ ˆ œ™[XZ[š[™×Ù]™[�ˆ -N‚ˆYŠZ[œ]Ù]™[�ˆXÛÛ[X[™Ú[œ]Ù]™[�ÛX]Ú\×Ü™\Üʈ[œ]ÜÝ\ ˆ[œ]Ù]™[� ˆXÝ[™×Ü^Y\‚ˆ -Bˆ -[œ]Ù]™[� Oœ™[X\ÙH ˆ[œ]ÜÝ\ Oœ™[X\ÙJBˆOH[œ]ÜÝ\ Oœ™[X\ÙBˆ -[œ]Ù]™[� OšÛ ˆ[œ]ÜÝ\ OšÛÝšYÙÙ\ŠBˆOH[œ]ÜÝ\ OšÛÝšYÙÙ\‚ˆ™]Ù\—Ù]™[�Ý[YH H[œ]Ù]™[� O�[YBˆˆÙ\]Y[˜ÙWÙܘXÙWÝ[YBˆXÛÛ[X[™Ú[œ]Ù]™[�ÛX]Ú\×ÚÛ -ˆ[œ]ÜÝ\ ˆ[œ]Ù]™[� ˆXÝ[™×Ü^Y\‚ˆ -JHˆ™]\›ˆ˜[ÙNˆB‚ˆYŠÙ\]Y[˜ÙWÚ[™^OH[™Ý -Hˆ -›X]ÚÝ[YHH[œ]Ù]™[� O�[YNˆ -›X]ÚØYÙHBˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�ˆ H™[XZ[š[™×Ù]™[�È H NˆB‚ˆ™]Ù\—Ù]™[�Ý[YHH[œ]Ù]™[� O�[YNˆB‚ˆ™]\›ˆ�YNŸB‚˜›ÛÛÚXÚרÛÛX›Ê×Ü^Y\ŠˆXÝ[™×Ü^Y\ŠH‚ˆ[œÚYÛ™Y[�Nˆ[�X^Ý\H LNˆ[�X^Ù^\ÈH LNˆ[�˜[YH LN‚ˆZ[� �ÝZ[—ÛX]ÚØYÙHHRS• �ÓPVˆZ[� �ÝX^ÛX]ÚÝ[YHH ˆÙ^WÛX\Ú×ÝÙ[XÝYÚÛÝšYÙÙ\—Ù›YÜÈH ‚ˆ›ÛÛX]ÚÙ›Ý[™H˜[ÙN‚ˆרÛÛH -˜ÛÛN‚ˆ›ÜŠHH ÈHÙ[‹O›[Ù[]KœÜXÚX[×ÛØYYÈJÊÊH‚ˆÛÛHHÙ[‹O›[Ù[]KœÜXÚX[ -ÈN‚ˆYŠÙ[‹O˜[š[X][Û‹O˜Ø[˜Ù[OHS’SPUSÓ—ÐÐS�ÑSÑTÐP“Q ‰‚ˆ -Ù[‹O˜[š[[�[HOHÛÛKO˜Ø[˜Ù[ˆÛÛKO™œ˜[YK›Z[ˆˆÙ[‹O˜[š[\ÜȈÛÛKO™œ˜[YK›X^Ù[‹O˜[š[\ÜȈÙ[‹O˜[š[X][Û‹Oš]ØÛÝ[�ÛÛKOš]ÊJHˆˆÛÛ�[�YNˆˆH[ÙHYŠÙ[‹O˜[š[X][Û‹O˜Ø[˜Ù[OHS’SPUSÓ—ÐÐS�ÑSÑTÐP“Qˆ ‰ˆ -ÛÛKO˜Ø[˜Ù[\^Y\—ØXØÙ\×ÚYWÚ[œ] -Ù[ŠHY™ŠÙ[‹OœÜÚ][Û‹žKÙ[‹O˜˜\ÙJHˆ JJHˆˆÛÛ�[�YNˆB‚ˆˆZ[� �ÝÛÛ[X[™ÛX]ÚØYÙNˆZ[� �ÝÛÛ[X[™ÛX]ÚÝ[YN‚ˆÛÛœÝZ[� �ÝÙ\]Y[˜ÙWÙܘXÙWÝ[YHBˆÛÛ[X[™ÜÙ\]Y[˜ÙWÙܘXÙWÝ[YWÙÙ] -ÛÛJN‚ˆYŠ˜[Y[š[JÙ[‹ÛÛKO˜[š[JBˆ ‰ˆ -ÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT ÛÛKO˜[š[JHÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÒ ÛÛKO˜[š[JJBˆ ‰ˆX]ÚÜÜXÚX[ØÛÛ[X[™ -ˆÛÛKOš[œ] ˆXÝ[™×Ü^Y\‹ˆÛÛKOœÝ\ˈÙ\]Y[˜ÙWÙܘXÙWÝ[YKˆ ˜ÛÛ[X[™ÛX]ÚÝ[YKˆ ˜ÛÛ[X[™ÛX]ÚØYÙBˆ -JH‚ˆ Ê‚ˆ -ˆ™Y™\ˆHÛÛ[X[™šYÙÙ\™YžHH™]Ù\݈ -ˆ[œ]]™[� ˆš[™ÈYÙH™\ÛÛ™\È]™[�È™XÛÜ™Yˆ -ˆ\š[™ÈHØ[YHÙÚXØ[XÚˈÛÛ[X[™ÈÚ\š[™Âˆ -ˆ]]™[�™]Z[ˆH^\Ý[™ÈÛ™Ù\Ý \Ù\]Y[˜ÙKˆ -ˆ[ˆÜ™X]\Ý XÚÜ™ \Ú^™H˜[šÚ[™Ë‚ˆ -‹ÂˆÛۜݛÛÛ™]\—ÛX]ÚBˆ[X]ÚÙ›Ý[™ˆÛÛ[X[™ÛX]ÚØYÙHZ[—ÛX]ÚØYÙBˆ -ÛÛ[X[™ÛX]ÚØYÙHOHZ[—ÛX]ÚØYÙBˆ ‰ˆ -ÛÛ[X[™ÛX]ÚÝ[YHˆX^ÛX]ÚÝ[YBˆ -ÛÛ[X[™ÛX]ÚÝ[YHOHX^ÛX]ÚÝ[YBˆ ‰ˆ -ÛÛKOœÝ\ȈX^Ý\ˆ -ÛÛKOœÝ\ÈOHX^Ý\ˆ ‰ˆÛÛKO›�[ZÙ^\ȈX^Ù^\ÊJJJJN‚ˆYŠX™]\—ÛX]Ú -HˆÛÛ�[�YNˆB‚ˆ˜[YHÛÛKO˜[š[NˆZ[—ÛX]ÚØYÙHHÛÛ[X[™ÛX]ÚØYÙNˆX^ÛX]ÚÝ[YHHÛÛ[X[™ÛX]ÚÝ[YNˆX^Ý\HÛÛKOœÝ\ΈX^Ù^\ÈHÛÛKO›�[ZÙ^\ΈÙ[XÝYÚÛÝšYÙÙ\—Ù›YÜÈBˆÛÛKOš[œ]ØÛÛKOœÝ\È H WKšÛÝšYÙÙ\ŽÂˆX]ÚÙ›Ý[™H�YNˆBˆB‚ˆB‚ˆYŠ˜[Y�H  ‰ˆÚXÚרÛÜÝ[Ý™J˜[Y  KÙ[‹Oš�[\[™ÊJHˆ Ê‚ˆ -ˆ]]ÛX]XÈ[YÙ\È\™HÛ™K\ÚÝ[œ]]™[�Ë‚ˆ -ˆÛÛœÝ[YHHÙ[XÝYš[˜[ \Ý\›YÜÈÛ›HY�\‚ˆ -ˆH[Ý™HÝ\�ÈÝXØÙ\ÜÙ�[Kˆ™\ÜË™[X\ÙKˆ -ˆ[™[œ™[]Y]]ÛX]XÈ›YÜÈ™[XZ[ˆ]˜Z[X›K‚ˆ -‹ÂˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÚÛÝšYÙÙ\ŠˆXÝ[™×Ü^Y\‹ˆZ[—ÛX]ÚØYÙKˆÙ[XÝYÚÛÝšYÙÙ\—Ù›Y܈ -N‚ˆ™]\›ˆ�YNˆB‚ˆ™]\›ˆ˜[ÙNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËL�ˆ HÜšYÚ[˜[HÜš][ˆžH�YÝYH -[šÛ›ÝÛˆ]JK‚Š‚Šˆ™]ÛÜšÈ]˜[X]\ÈÚ]\ˆH^Y\ˆŠˆ\ÈH[™[™ÈÛÛ[X[™Ù\]Y[˜ÙHÈ^XÝ]K‚Š‹Â˜›ÛÛ^Y\—Ü™Z[œ] - -Hˆˆ×Ü^Y\ˆ -˜XÝ[™×Ü^Y\ˆH^Y\ˆ -ÈÙ[‹Oœ^Y\š[™^‚ˆYŠÚXÚרÛÛX›ÊXÝ[™×Ü^Y\ŠJHˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÐÓÓ•“ÓÑVT΂ˆ™]\›ˆ�YNˆB‚ˆ™]\›ˆ˜[ÙNŸB‚‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËLL‹LŽHŠˆŠˆÝÜ�[›š[™ÈYˆ^Y\‚Šˆ›ÈÛ™Ù\ˆ[œ]È\™XÝ[Û‚Šˆ]Ý\�YH�[‹‚Š‹Â�›ÚY�[—ÝžWÜ�[œÝÜØÚXÚÊ[�]JˆXÝ[™×Ù[�]KÛÛœÝWÔ�[–\™XÝ[Ûˆ[Ý™^ ÛÛœÝWÔ�[–‘\™XÝ[Ûˆ[Ý™^‹ÛÛœÝWÔ�[–\™XÝ[Ûˆ�[›š[™×Þ ÛÛœÝWÔ�[–‘\™XÝ[Ûˆ�[›š[™×Þ‹ÛÛœÝ[��[�ÛÛ™šYÑ›YÜËÛÛœÝ[�\ÚÛÛ[X[™›YËÛÛœÝ[�\Úš^Y›YËÛÛœÝ[�[˜X›Y›YËÛÛœÝ[�ÝÜÝ]Q›YÊHˆYˆ -J�[�ÛÛ™šYÑ›YÜÈ ˆ[˜X›Y›YÊHˆ -\Úš^Y›YÈ ‰ˆXXÝ[™×Ù[�]KO˜[š[X][™ÊBˆ - -\ÚÛÛ[X[™›YÈ ‰ˆXXÝ[™×Ù[�]KO˜[š[X][™ÊBˆ -Y™Š[Ý™^ �[›š[™×Þ -H ‰ˆ -[Ý™^ˆOH•S—ÑT—Ö—Ó“Ó‘HXÝ[™×Ù[�]KOœ�[›š[™È ˆ•S—ÔÕUWÔÕT•Ö -JBˆ -Y™Š[Ý™^‹�[›š[™×ÞŠH ‰ˆ -[Ý™^OH•S—ÑT—ÖÓ“Ó‘HXÝ[™×Ù[�]KOœ�[›š[™È ˆ•S—ÔÕUWÔÕT•ÖŠJJJHˆXÝ[™×Ù[�]KOœ�[›š[™ÈHÝÜÝ]Q›YΈBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËLL‹LŽBŠˆŠˆÚXÚÈ^Y\ˆ[œ][™�[ˆÝÜŠˆ�[˜Ý[ÛˆYˆ™YYY ‚Š‹Â�›ÚY�[—ÝžWÜ�[œÝÜÜ^Y\Š[�]JˆXÝ[™×Ù[�]KÛÛœÝ×Ü^Y\ŠˆXÝ[™×Ü^Y\ŠBžÈˆÛÛœÝWÜ�[—ÜÝ]H�[—ÜÝ]HHXÝ[™×Ù[�]KOœ�[›š[™Î‚ˆYˆ -�[—ÜÝ]HOH•S—ÔÕUWÓ“Ó‘JBˆˆ™]\›ŽÂˆB‚ˆ Ê‚ˆ -ˆÙ]^Y\ˆ\™XÝ[Û˜[[œ]ˆ -ˆœ›ÛHÙ^HÝ]\Ë‚ˆ -‹Â‚ˆÛÛœÝWÔ�[–‘\™XÝ[Ûˆ[Ý™^ˆH -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘UT -HÈ•S—ÑT—Ö—ÕTˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠHÈ•S—ÑT—Ö—ÑÕÓˆˆ•S—ÑT—Ö—Ó“Ó‘NˆÛÛœÝWÔ�[–\™XÝ[Ûˆ[Ý™^H -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• -HÈ•S—ÑT—ÖÓQ•ˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -HÈ•S—ÑT—ÖÔ’QÒˆ•S—ÑT—ÖÓ“Ó‘N‚ˆ Ê‚ˆ -ˆÙ]H�[›š[™È\™XÝ[Ûˆœ›ÛBˆ -ˆ™[ØÚ]K‚ˆ -‹Â‚ˆÛÛœÝWÔ�[–‘\™XÝ[Ûˆ�[›š[™×ÞˆH�[—ÜÝ]HÈ - -XÝ[™×Ù[�]KO�™[ØÚ]Kžˆ -HÈ•S—ÑT—Ö—ÕTˆ -XÝ[™×Ù[�]KO�™[ØÚ]Kžˆˆ -HÈ•S—ÑT—Ö—ÑÕÓˆˆ•S—ÑT—Ö—Ó“Ó‘JHˆ•S—ÑT—Ö—Ó“Ó‘NˆÛÛœÝWÔ�[–\™XÝ[Ûˆ�[›š[™×ÞH�[—ÜÝ]HÈ - -XÝ[™×Ù[�]KO�™[ØÚ]Kž -HÈ•S—ÑT—ÖÓQ•ˆ -XÝ[™×Ù[�]KO�™[ØÚ]Kžˆ -HÈ•S—ÑT—ÖÔ’QÒˆ•S—ÑT—ÖÓ“Ó‘JHˆ•S—ÑT—ÖÓ“Ó‘N‚ˆ ʈ�\ݛ܈™XYXš[]H™[Ýˈ -‹ÂˆÛÛœÝWÜ�[—ØÛÛ™šY×Ù›YÜÈXÝ[™×Ü�[—ØÛÛ™šY×Ù›YÜÈHXÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜ΂ˆ Ê‚ˆ -ˆÝÜ�[›š[™Ïˆ -‚ˆ -ˆ Kˆ^\È\™XÝ[Û‹‚ˆ -ˆ ‹ˆX›HÈ�[ˆ[ˆ\™XÝ[Û�ˆ -ˆ ˈš^Y\ÚÈš^Y\Ú\ÈÝ܈ -ˆÚ[ˆH[š[X][ÛˆÛÛ\]\ˈ^Bˆ -ˆYۛܙH™[X\Ú[™ÈHÙ^H[™È›Ýˆ -ˆÝÜÛˆH\œ[™XÝ[\ˆ^\ÈÛÛ[X[™ˆ -ˆ]™[ˆYˆÙHØ[‰Ý[Ý™HÛˆHÝ\‚ˆ -ˆ^\ˈ›ÝHHÝ\ˆ^\È\ÈYۛܙYˆ -ˆžHÙÚXÈÝۜݙX[K‚ˆ -‚ˆ -ˆ ˆÝÜÛˆ›ÛÝÚ[™Î‚ˆ -ˆ H›È\™XÝ[ÛˆÛÛ[X[™ ‚ˆ -ˆ H\™XÝ[ÛˆÛÛ[X[™ÜÜÙ\ÈÝ\œ™[�\™XÝ[Û‹‚ˆ -ˆ H\œ[™XÝ[\ˆ^\ÈÛÛ[X[™Ú[ˆÙHÛ‰Ý]™HH›YÈ[˜X›Y ‚ˆ -‚ˆ -ˆ™\X]ÚXÚț܈ÜÜÚ]H\™XÝ[Û‚ˆ -ˆ[™›Üˆˆ^\Ë‚ˆ -‹Â‚ˆÝÚ]Ú -�[›š[™×Þ -HˆØ\ÙH•S—ÑT—ÖÓQ•‚ˆ�[—ÝžWÜ�[œÝÜØÚXÚÊXÝ[™×Ù[�]K[Ý™^ [Ý™^‹�[›š[™×Þ �[›š[™×Þ‹XÝ[™×Ü�[—ØÛÛ™šY×Ù›YÜË•S—ÐÓÓ‘’Q×ÖÓQ•ÑTÒÐÓÓSPS‘ •S—ÐÓÓ‘’Q×ÖÓQ•ÑTÒÑ’VQ •S—ÐÓÓ‘’Q×ÖÓQ•ÑS�P“Q •S—ÔÕUWÓ“Ó‘JNˆœ™XZ΂ˆØ\ÙH•S—ÑT—ÖÔ’QÒ‚ˆ�[—ÝžWÜ�[œÝÜØÚXÚÊXÝ[™×Ù[�]K[Ý™^ [Ý™^‹�[›š[™×Þ �[›š[™×Þ‹XÝ[™×Ü�[—ØÛÛ™šY×Ù›YÜË•S—ÐÓÓ‘’Q×ÖÔ’QÒÑTÒÐÓÓSPS‘ •S—ÐÓÓ‘’Q×ÖÔ’QÒÑTÒÑ’VQ •S—ÐÓÓ‘’Q×ÖÔ’QÒÑS�P“Q •S—ÔÕUWÓ“Ó‘JNˆœ™XZ΂ˆØ\ÙH•S—ÑT—ÖÓ“Ó‘N‚ˆœ™XZΈB‚ˆÝÚ]Ú -�[›š[™×ÞŠHˆØ\ÙH•S—ÑT—Ö—ÕT‚ˆ�[—ÝžWÜ�[œÝÜØÚXÚÊXÝ[™×Ù[�]K[Ý™^ [Ý™^‹�[›š[™×Þ �[›š[™×Þ‹XÝ[™×Ü�[—ØÛÛ™šY×Ù›YÜË•S—ÐÓÓ‘’Q×Ö—ÕTÑTÒÐÓÓSPS‘ •S—ÐÓÓ‘’Q×Ö—ÕTÑTÒÑ’VQ •S—ÐÓÓ‘’Q×Ö—ÕTÑS�P“Q •S—ÔÕUWÓ“Ó‘JNˆœ™XZ΂ˆØ\ÙH•S—ÑT—Ö—ÑÕÓŽ‚ˆ�[—ÝžWÜ�[œÝÜØÚXÚÊXÝ[™×Ù[�]K[Ý™^ [Ý™^‹�[›š[™×Þ �[›š[™×Þ‹XÝ[™×Ü�[—ØÛÛ™šY×Ù›YÜË•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑTÒÐÓÓSPS‘ •S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑTÒÑ’VQ •S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑS�P“Q •S—ÔÕUWÓ“Ó‘JNˆœ™XZ΂ˆØ\ÙH•S—ÑT—Ö—Ó“Ó‘N‚ˆœ™XZΈBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLM‚Š‚Šˆ™]\›ˆ�YHÚ[ˆH[�]HX^HŠˆ›ØÙ\ÜÈÜ™[˜\žHYK\Ý]H^Y\ˆŠˆ[œ] ‚Š‹Â˜›ÛÛ^Y\—ØXØÙ\×ÚYWÚ[œ] -ÛÛœÝ[�]H -˜XÝ[™×Ù[�]JHˆˆÛÛœÝר[š[H -˜[š[X][ÛˆHXÝ[™×Ù[�]KO˜[š[X][ێ‚ˆYŠXÝ[™×Ù[�]KOšY[™ÊHˆ™]\›ˆ�YNˆB‚ˆYŠX[š[X][Û‚ˆX[š[X][Û‹OšYBˆ[š[X][Û‹O›�[Yœ˜[Y\ÈH ˆXÝ[™×Ù[�]KO˜[š[\܈�H -Z[� �Ý -X[š[X][Û‹O›�[Yœ˜[Y\ÊHˆ™]\›ˆ˜[ÙNˆB‚ˆ™]\›ˆ[š[X][Û‹OšYVØXÝ[™×Ù[�]KO˜[š[\Ü×HÈ�YHˆ˜[ÙNŸB‚�›ÚY^Y\—Ý[šÊ -BžÂˆ\YYˆ[�[HWÛØØ[ØXÝ[Û—Ù›Y܈ˆPÕSÓ—Ó“Ó‘KˆPÕSÓ—ÕÐSˈPÕSÓ—ÕT ˆPÕSÓ—ÑÕÓ‹ˆPÕSÓ—Ô•S‚ˆHWÛØØ[ØXÝ[Û—Ù›YÜ΂ˆWÛØØ[ØXÝ[Û—Ù›YÜÈXÝ[ÛˆH ˆ›ÛÛ˜XÚ×ÝØ[ÈH˜[ÙNÈ ËØ˜XÚÝØ[ˆˆ[�]H -›Ý\ˆH•Sˆ›Ø][š]X[Ú�[\Ý™[ØÚ]WÞˆH Œ‚ˆ[�]JˆXÝ[™×Ù[�]HHÙ[ŽÂ‚ˆÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙWÛY�ÛY�×HHÑ“Q×ÓSÕ‘SQ• “Q×ÓSÕ‘SQ•NˆÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙWÜšYÚÜšYÚ×HHÑ“Q×ÓSÕ‘T’QÒ “Q×ÓSÕ‘T’QÒNˆÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙWÝ\Ý\×HHÑ“Q×ÓSÕ‘UT “Q×ÓSÕ‘UTNˆÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙWÙÝÛ—ÙÝÛ–×HHÑ“Q×ÓSÕ‘QÕÓ‹“Q×ÓSÕ‘QÕÓŸNȈˆ[�HHXÝ[™×Ù[�]KOœ^Y\š[™^ˆ×Ü^Y\ˆ -˜XÝ[™×Ü^Y\ˆH^Y\ˆ -ÈN‚ˆYŠXÝ[™×Ü^Y\‹O™[�OHXÝ[™×Ù[�]HXÝ[™×Ù[�]KO™X]ÜÝ]H ˆPUÔÕUWÑPQ -Bˆˆ™]\›ŽÂˆB‚ˆ Ê‚ˆ -ˆ\™HÙHÝXÚ[™ÈH[™]™[[�]Oˆ -‹ÂˆYŠ -Ý\ˆHš[™Ù[�Ú\™JXÝ[™×Ù[�]KXÝ[™×Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž‹TWÑS‘U‘S •S -JHˆ ‰ˆY™ŠXÝ[™×Ù[�]KOœÜÚ][Û‹žKÝ\‹OœÜÚ][Û‹žJHH ŒJH‚ˆ›ÛÛ›×Ü^Y\—Ü™XXÚYH˜[ÙNˆZ[� �ÝÝ[WÛ›ÝÜ™XXÚYH ˆZ[� �ÝÝ[WÜ™XXÚYH ˆZ[� �ÝNˆˆ›Üˆ -HH ÈHPVÔVQT”ÎÈJÊÊHˆYˆ -\™XXÚYÚWJ^ˆÝ[WÛ›ÝÜ™XXÚY -ÊΈBˆBˆ›×Ü^Y\—Ü™XXÚYH -Ý[WÛ›ÝÜ™XXÚY�HPVÔVQT”ÊHÈ�YHˆ˜[ÙN‚ˆYŠ›×Ü^Y\—Ü™XXÚY -HˆYØÛÜ™JKÝ\‹O›[Ù[]KœØÛÜ™JNˆBˆ™XXÚYÜWHH�YN‚ˆ›Üˆ -HH ÈHPVÔVQT”ÎÈJÊÊHˆÝ[WÜ™XXÚY -ÏH™XXÚYÚWNˆB‚ˆYˆ -[Ý\‹O›[Ù[]KœÝX�\H -Ý\‹O›[Ù[]KœÝX�\HOHÕP•TWГÕ ‰ˆÝ[WÜ™XXÚY�H -ÛÝ[�Ù[�ÊTWÔVQTŠJJJHˆ]™[ØÛÛ\]YH N‚ˆYŠÝ\‹O›[Ù[]K˜œ˜[˜Ú -HˆÝ›˜ÜJœ˜[˜ÚÛ˜[YKÝ\‹O›[Ù[]K˜œ˜[˜Ú PVÓ�SQWÓSŠNÈ ËÛ›ÝË[ÝHØ[ˆœ˜[˜ÚÈ[›Ý\ˆ]™[ˆBˆ™]\›ŽÂˆBˆB‚ˆ Ê‚ˆ -ˆ™\Ù]ÛÛX›ÈÛÝ[�Yˆ[YH\È^\™Y ‚ˆ -‹ÂˆYŠÝ[YHˆXÝ[™×Ù[�]KOœ�\Ú �[YJHˆXÝ[™×Ù[�]KOœ�\Ú ˜ÛÝ[�H ˆXÝ[™×Ù[�]KOœ�\Ú �[YHH ˆB‚ˆYŠ^Y\—Ü™Z[œ] - -JHˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ù[�]KO˜Ú\™Ú[™ÊHˆ^Y\—ØÚ\™ÙWØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ù[�]KOš[œZ[ˆ ˆ’S—ÔRS—Ó“Ó‘H -XÝ[™×Ù[�]KO›[šÈ ‰ˆXXÝ[™×Ù[�]KO™Ü˜X˜š[™ÊJHˆ^Y\—ÜZ[—ØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ ËȘ[[™ÏÈÚXÚț܈[™[™ÂˆYŠXÝ[™×Ù[�]KOœ›Ú™XÝ[H ˆ“TÕÕÔÔÊHˆ^Y\—Ù˜[ØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ ËÈܘXˆÙXÝ[Û‹Û�[Ý™HYˆÝ[[š[X][™ÂˆYŠXÝ[™×Ù[�]KO™Ü˜X˜š[™È ‰ˆXÝ[™×Ù[�]KO˜]XÚÚ[™ÈOHUPÒÒS‘×Ó“Ó‘H ‰ˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆOHÛÛ[[Û—Ý›Ý×ÝØZ] -Hˆ^Y\—ÙܘX—ØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ ËÈ�[\ÙXÝ[Û‚ˆYŠXÝ[™×Ù[�]KOš�[\[™ÊHˆ^Y\—Ú�[\ØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ù[�]KO˜[š[[�[HOHS’WÕÐSÓÑ‘ŠHˆ^Y\—ÝØ[ÛÙ™—ØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ù[�]KO™›Ü ‰ˆXÝ[™×Ù[�]KOœÜÚ][Û‹žHOHXÝ[™×Ù[�]KO˜˜\ÙH ‰ˆXXÝ[™×Ù[�]KO�™[ØÚ]KžJHˆ^Y\—ÛYWØÚXÚÊ -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ Ê‚ˆ -ˆÚXÚÈYˆ^Y\ˆ\È[ˆHÝ]H][Ý܈ -ˆÜ™[˜\žHYK\Ý]H[œ]›ØÙ\ÜÚ[™ËˆYˆ›Ý ˆ -ˆÚÚ\H™\ÝÙˆH[œ]ÚXÚÜË‚ˆ -‹ÂˆYŠ\^Y\—ØXØÙ\×ÚYWÚ[œ] -XÝ[™×Ù[�]JJHˆÛÝÈ[™[šØÚXÚΈB‚ˆ ËÈÚXÚÈYˆ[�]H\È[™\ˆH]›Ü›Bˆ ÊšYŠXÝ[™×Ù[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÔU“Ô“H ‰ˆ -ZYÚ˜\ˆHXÝ[™×Ù[�]KO˜[š[X][Û‹OœÚ^™KžHÈXÝ[™×Ù[�]KO˜[š[X][Û‹OœÚ^™KžHˆXÝ[™×Ù[�]KO›[Ù[]KœÚ^™KžJH ‰‚ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÑPÒÊH ‰ˆÚXÚ×Ü]›Ü›WØ™]ÙY[ŠXÝ[™×Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]KOœÜÚ][Û‹žKXÝ[™×Ù[�]KOœÜÚ][Û‹žH -ÈZYÚ˜\‹XÝ[™×Ù[�]JJBˆˆXÝ[™×Ù[�]KOšY[™ÈHQS‘×Ó“Ó‘NˆXÝ[™×Ù[�]KO™XÚÚ[™ÈHPÒ×ÐPÕU‘NˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÜÝXÚ×Ý[™\›™X]ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑPÒË -NˆÛÝÈ[™[šØÚXÚΈJ‹Â‚ˆÛۜݛØ][Y™ˆHY™ŠXÝ[™×Ù[�]KOœÜÚ][Û‹žKXÝ[™×Ù[�]KO˜˜\ÙJNˆÛۜݛÛÛ›Ý[˜Z\ˆH -XÝ[™×Ù[�]KO›[™YÛÛ—Ü]›Ü›HÈ[Y™ˆ Hˆ[Y™ˆ ŠN‚ˆYŠXÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×ÓSÕ‘UT -H‚ˆÛۜݛÛÛÛÛ[X[™ÛX]ÚH -›Ý[˜Z\ˆ ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÝ\Ý\ XÝ[™×Ü^Y\‹ ŠJN‚ˆYŠÛÛ[X[™ÛX]Ú ‰ˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ -•S—ÐÓÓ‘’Q×Ö—ÕTÑS�P“Q•S—ÐÓÓ‘’Q×Ö—ÕTÒS’UPS -JHOH -•S—ÐÓÓ‘’Q×Ö—ÕTÑS�P“Q•S—ÐÓÓ‘’Q×Ö—ÕTÒS’UPS -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•SŠJHˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘UTˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÔÕT•ÖÈ ËÈ^Y\ˆ™YÚ[œÈÈ�[‚ˆˆH[ÙHYŠÛÛ[X[™ÛX]Ú ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒÕT -JH‚ˆ ËÈ™]ÈHHÛÛX›È]XÚˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘UTˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐUPÒÕT  -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNÈ ËÈ\ÈÛÜšØ\›Ý[™X[ÈY˜][œ™Y\ÜXÚX[ ‚ˆÛÝÈ[™[šØÚXÚΈˆH[ÙHYŠÛÛ[X[™ÛX]Ú ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÑÑÑJJHˆ ËÈ™]ÈÙÙH[Ý™HZÙHÛˆÓԌˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘UTˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙÙÙNˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOšY[™ÈHQS‘×Ó“Ó‘NˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH XXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž -ˆ K�ÍNˆXÝ[™×Ù[�]KO�™[ØÚ]KžH ËËÈÒÈ[ÝHØ[ˆ\ÙH�[\œ˜[YHÈ[ÙYžH\È[ž]Ø^Bˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑÑÑK -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆYŠXÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠHˆÛۜݛÛÛÛÛ[X[™ÛX]ÚH -›Ý[˜Z\ˆ ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÙÝÛ—ÙÝÛ‹XÝ[™×Ü^Y\‹ ŠJN‚ˆYŠÛÛ[X[™ÛX]Ú ‰ˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ -•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑS�P“Q•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÒS’UPS -JHOH -•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑS�P“Q•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÒS’UPS -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•SŠJHˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘QÕӎˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÔÕT•ÖŽÈ ËÈ^Y\ˆ™YÚ[œÈÈ�[‚ˆˆH[ÙHYŠÛÛ[X[™ÛX]Ú ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒÑÕÓŠJHˆ ËÈ™]ÈÛÛX›È]XÚˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘QÕӎˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐUPÒÑÕÓ‹ -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆÛÝÈ[™[šØÚXÚΈˆH[ÙHYŠÛÛ[X[™ÛX]Ú ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÑÑÑJJHˆ ËÈ™]ÈÙÙH[Ý™HZÙHÛˆÓԌˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÓSÕ‘QÕӎˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙÙÙNˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOšY[™ÈHQS‘×Ó“Ó‘NˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆHXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž -ˆ K�ÍNˆXÝ[™×Ù[�]KO�™[ØÚ]KžH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑÑÑK -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆYŠ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -JJHˆˆÛۜݛÛÛÛÛ[X[™ÛX]ÚÛY�H -›Ý[˜Z\ˆ ‰ˆ -XÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÛY�ÛY� XÝ[™×Ü^Y\‹ ŠJJNˆÛۜݛÛÛÛÛ[X[™ÛX]ÚÜšYÚH -›Ý[˜Z\ˆ ‰ˆ -XÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÜšYÚÜšYÚ XÝ[™×Ü^Y\‹ ŠJJNˆÛۜݛÛÛÛÛ[X[™ÛX]ÚÙ›Ü�Ø\™HÛÛ[X[™ÛX]ÚÛY�ÛÛ[X[™ÛX]ÚÜšYÚˆÛۜݛÛÛÛÛ[X[™ÛX]ÚؘXÚÈH›Ý[˜Z\‚ˆ ‰ˆXÝ[™×Ù[�]KO›[Ù[]K™˜XÚ[™Âˆ ‰ˆ - -XÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÛY�ÛY� XÝ[™×Ü^Y\‹ ŠJBˆ -XÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWÜšYÚÜšYÚ XÝ[™×Ü^Y\‹ ŠJJNȈˆYˆ -ÛÛ[X[™ÛX]ÚÛY� ‰ˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ -•S—ÐÓÓ‘’Q×ÖÓQ•ÑS�P“Q•S—ÐÓÓ‘’Q×ÖÓQ•ÒS’UPS -JHOH -•S—ÐÓÓ‘’Q×ÖÓQ•ÑS�P“Q•S—ÐÓÓ‘’Q×ÖÓQ•ÒS’UPS -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•SŠJH‚ˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HŠ“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -NÈ ËÈ\ÝX[HY� -ÈšYÚ\È›ÝXØÙ\X›KÛÈ]\ÈÒÈÈ�[›ÝˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÔÕT•ÖÈ ËÈ^Y\ˆ™YÚ[œÈÈ�[‚ˆˆH[ÙHYŠÛÛ[X[™ÛX]ÚÜšYÚ ‰ˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ -•S—ÐÓÓ‘’Q×ÖÔ’QÒÑS�P“Q•S—ÐÓÓ‘’Q×ÖÔ’QÒÒS’UPS -JHOH -•S—ÐÓÓ‘’Q×ÖÔ’QÒÑS�P“Q•S—ÐÓÓ‘’Q×ÖÔ’QÒÒS’UPS -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•SŠJHˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HŠ“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -NÈ ËÈ\ÝX[HY� -ÈšYÚ\È›ÝXØÙ\X›KÛÈ]\ÈÒÈÈ�[›ÝˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÔÕT•ÖÈ ËÈ^Y\ˆ™YÚ[œÈÈ�[‚ˆˆH[ÙHYŠÛÛ[X[™ÛX]ÚؘXÚÈ ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐ�PÒÔ•SŠJHˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HŠ“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -NÈ ËÈ\ÝX[HY� -ÈšYÚ\È›ÝXØÙ\X›KÛÈ]\ÈÒÈÈ�[›ÝˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÔÕT•ÖÈ ËÈ^Y\ˆ™YÚ[œÈÈ�[‚ˆˆH[ÙHYŠÛÛ[X[™ÛX]ÚÙ›Ü�Ø\™ ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒѓԕÐT‘ -JHˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HŠ“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -NˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐUPÒѓԕÐT‘  -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWØÛÛœÝ[YWÛ]\ÝÜ™\ÜÊXÝ[™×Ü^Y\ŠNˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆ Ê‚ˆ -ˆ]XÚÈ›Ý ˆ]XÚÈ -ÈÙ^HX\Yˆ -ˆžHZœÜXÚX[ ‚ˆ -‹Â‚ˆYŠ -Ûؘ[ØÛÛ™šY˘ZœÜXÚX[OHR”ÔPÒPSÒÑVWÔÔPÒPS ‰ˆ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒГÕ -J_ˆ -Ûؘ[ØÛÛ™šY˘ZœÜXÚX[OHR”ÔPÒPSÒÑVWÐUPÒ̈ ‰ˆ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒГÕ -J_ˆ -Ûؘ[ØÛÛ™šY˘ZœÜXÚX[OHR”ÔPÒPSÒÑVWÐUPÒÌÈ ‰ˆ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒГÕ -J_ˆ -Ûؘ[ØÛÛ™šY˘ZœÜXÚX[OHR”ÔPÒPSÒÑVWÐUPÒÍ ‰ˆ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒГÕ -JJBˆˆYŠ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÐUPÒÊH ‰ˆ›Ý[˜Z\ŠBˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×Ò•STˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOœÝ[[YHH È ËÈYˆ]XÚÈ\È™\ÜÙY Û[™ÈÝÛˆ]XÚÈÈ^XÝ]H]XÚÌÈ\È›ÈÛ™Ù\ˆ˜[Yˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐUPÒГÕ  -NˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆ Ê‚ˆ -ˆÛÛ[X[™TÚ\™ÙK‚ˆ -‹Â‚ˆYŠ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST -H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑJJBˆˆYŠ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÔÔPÒPS -H ‰ˆ›Ý[˜Z\ŠBˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×Ò•STˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ØÚ\™ÙNˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KOœÝ[[YHH ˆÙ]ØÚ\™Ú[™ÊXÝ[™×Ù[�]JNˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑK -NˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆ Ê‚ˆ -ˆ[™HÜXÚX[Ù^KˆÚXÚț܈\�ÙˆHˆ -ˆœ™Y\ÜXÚX[ÛÛ[X[™ [™[ˆ›ØÚÚ[™Ë‚ˆ -‹Â‚ˆYŠXÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×ÔÔPÒPS -H ËÈHÜXÚX[�]ÛˆØ[ˆ›ÝÈ™H\ÙY›Üˆœ™Y\ÜXÚX[ˆˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÔÔPÒPS ŠH ‰ˆ›Ý[˜Z\ˆ ‰‚ˆ -XÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ•ˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• -H‚ˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JH -BˆˆYŠÚXÚרÛÜÝ[Ý™JS’WÔÔPÒPS ‹  -JBˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÔÔPÒPSˆÛÝÈ[™[šØÚXÚΈBˆB‚‚BKËÈ›ØÚÚ[™Ë‚ˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WГÐÒÊH ‰ˆ›Ý[˜Z\ˆ ‰ˆJXÝ[™×Ù[�]KO›[Ù[]K˜›ØÚרÛÛ™šY×Ù›YÜÈ ˆ“ÐÒ×ÐÓÓ‘’Q×ÑTÐP“Q -JBˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÔÔPÒPS‚‚BBKËÈÙ]\›YÜËXÝ[Û‹[™›ØÚÈ[š[X][ۜ˂‚BBYרXÝ]™WØ›ØÚÊXÝ[™×Ù[�]JN‚‚BBYÛÝÈ[™[šØÚXÚΈBˆB‚ˆ Ê‚ˆ -ˆœ™XZÛÝ]ÜXÚX[ ‚ˆ -‹Â‚ˆYŠ›Ý[˜Z\ˆ ‰ˆ^Y\—ØÚXÚ×ÜÜXÚX[ - -JBˆˆÛÝÈ[™[šØÚXÚÎÈ ËÈÛÈ[ÝHÛ‰Ý\™›Ü›HÜXÚX[Ș[[™ÈÙ™ˆHYÙBˆB‚ˆ Ê‚ˆ -ˆÚ\™ÙH]XÚÈ -Û]XÚÈ ˆ™[X\ÙJK‚ˆ -‹Â‚ˆYŠ -XÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\È ˆ“Q×ÐUPÒÊJBˆˆYŠXÝ[™×Ù[�]KOœÝ[[YH ‰ˆ›Ý[˜Z\ˆ ‰‚ˆ - -˜[Y[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑPUPÒÊH ‰ˆXÝ[™×Ù[�]KOœÝ[[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆXÝ[™×Ù[�]KO›[Ù[]K˜[š[X][Û–ÐS’WÐÒT‘ÑPUPÒ×KO˜Ú\™ÙWÝ[YJHÝ[YJHˆ -]˜[Y[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑPUPÒÊH ‰ˆ˜[Y[š[JXÝ[™×Ù[�]K[š[X]XÚÜÖØXÝ[™×Ù[�]KO›[Ù[]K˜]ÚZ[–ØXÝ[™×Ù[�]KO›[Ù[]K˜ÚZ[›[™Ý H WH H WJBˆ ‰ˆXÝ[™×Ù[�]KO›[Ù[]K˜ÚZ[›[™Ýˆ  ‰ˆXÝ[™×Ù[�]KOœÝ[[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆXÝ[™×Ù[�]KO›[Ù[]K˜[š[X][Û–Ø[š[X]XÚÜÖØXÝ[™×Ù[�]KO›[Ù[]K˜]ÚZ[–ØXÝ[™×Ù[�]KO›[Ù[]K˜ÚZ[›[™Ý H WH H WWKO˜Ú\™ÙWÝ[YJHÝ[YJJJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ‚ˆXÝ[™×Ù[�]KOœÝ[[YHH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý œ[˜Ú�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý œ[˜Ú  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑPUPÒÊJBˆˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐÒT‘ÑPUPÒË -NˆBˆ[ÙHYŠ˜[Y[š[JXÝ[™×Ù[�]K[š[X]XÚÜÖØXÝ[™×Ù[�]KO›[Ù[]K˜]ÚZ[–ØXÝ[™×Ù[�]KO›[Ù[]K˜ÚZ[›[™Ý H WH H WJJBˆˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]K[š[X]XÚÜÖØXÝ[™×Ù[�]KO›[Ù[]K˜]ÚZ[–ØXÝ[™×Ù[�]KO›[Ù[]K˜ÚZ[›[™Ý H WH H WK -NˆBˆÛÝÈ[™[šØÚXÚΈBˆXÝ[™×Ù[�]KOœÝ[[YHH ˆB‚ˆ Ê‚ˆ -ˆ]XÚÈ�]Û‹ˆ[™H˜\ÚXÈ]XÚȈ -ˆ�]ÛˆXÝ[ÛœÈ -]XÚËÙ] ]ËŠK‚ˆ -‹Â‚ˆYŠ -XÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×ÐUPÒÊH ‰ˆ›Ý[˜Z\ŠBˆˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×ÐUPÒΈXÝ[™×Ù[�]KOœÝ[[YHH È ËÈ\ØX›HH]XÚÌÈÝ[[YB‚ˆYŠ -XÝ[™×Ù[�]KO™XÚÚ[™È ˆPÒ×ÐPÕU‘JH ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÑPÒÐUPÒÊH ‰ˆVQT—ÓRS—ÖˆOHVQT—ÓPVÖŠH ËØXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓ‚ˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑPÒÐUPÒË -NˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ù[�]KOœ�[›š[™È ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•S�UPÒÊJH ËÈ™]È�[ˆ]XÚÈÛÙHÙXÝ[Û‚ˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘Nˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÔ•S�UPÒË -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ Ê‚ˆ -ˆ˜XÚÈ]XÚˈYˆ^Y\‰ÜÈÛÛ[X[™�Y™™\ˆX]Ú\Ȉ -ˆH˜XÚÈ]XÚÈÙ\]Y[˜ÙKÙIÛ][\ÈȈ -ˆH˜XÚÈ]XÚË‚ˆ -‹ÂˆÛÛœÝÙ^WÛX\Ú×ÝÙ\]Y[˜ÙWؘXÚר]XÚÖ×HHÑ“Q×Ð�PÒÕÐT‘ “Q×ÐUPÒßN‚ˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÐUPÒÐ�PÒÕÐT‘ -H ‰ˆX]ÚØÛÛX›ÊÙ\]Y[˜ÙWؘXÚר]XÚËXÝ[™×Ü^Y\‹ ŠJHˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ]XÚ×Ú[œ]Ù]™[�ˆÛÛœÝרÛÛ[X[™Ú[œ]Ù]™[� -ˆ˜XÚÝØ\™Ú[œ]Ù]™[�‚ˆZ[� �Ý\ÝÜžWÚ[™^BˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^‚ˆZ[� �Ý™[XZ[š[™×Ù]™[�ÈBˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�‚ˆ Ê‚ˆ -ˆÙ]H\ÝÛÈ™\ÜÈ]™[�ˈ™XY[™È›ÝYÚˆ -ˆHÛÛ\]Xš[]H[\ˆ™]™[�È�]\™H™[X\ÙBˆ -ˆ[™[YYÛ]™[�Èœ›ÛH\Ý\˜š[™È˜XÚÈ]XÚË‚ˆ -‹Âˆ]XÚ×Ú[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×Ü™\ÜʈXÝ[™×Ü^Y\‹ˆ š\ÝÜžWÚ[™^ ˆ œ™[XZ[š[™×Ù]™[�ˆ -N‚ˆ˜XÚÝØ\™Ú[œ]Ù]™[�HÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ™]š[Ý\×Ü™\ÜʈXÝ[™×Ü^Y\‹ˆ š\ÝÜžWÚ[™^ ˆ œ™[XZ[š[™×Ù]™[�ˆ -N‚ˆ Ê‚ˆ -ˆY˜XÚÈ]XÚÈ[œ]ÈÛÛYHÚ][ˆBˆ -ˆ[YHÚ[™ÝÏÈYˆÛËÙIÛÈH˜XÚÈ]XÚË‚ˆ -‹ÂˆYŠ]XÚ×Ú[œ]Ù]™[�ˆ ‰ˆ˜XÚÝØ\™Ú[œ]Ù]™[�ˆ ‰ˆ]XÚ×Ú[œ]Ù]™[� O�[YBˆ H˜XÚÝØ\™Ú[œ]Ù]™[� O�[YBˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY È L -H‚ˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžH ˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ‚ˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ•ˆ ‰ˆ -˜XÚÝØ\™Ú[œ]Ù]™[� Oœ™\ÜÈ ˆ“Q×ÓSÕ‘SQ• -JHˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆˆH[ÙHYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒˆ ‰ˆ -˜XÚÝØ\™Ú[œ]Ù]™[� Oœ™\ÜÈ ˆ“Q×ÓSÕ‘T’QÒ -JHˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆB‚ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐUPÒÐ�PÒÕÐT‘  -NˆÛÝÈ[™[šØÚXÚΈBˆB‚ˆ Ê‚ˆ -ˆÙ]][KˆYˆ^Y\ˆ\ÈÝ[™[™ÈÛˆ[ˆ][Bˆ -ˆ[™HÙ][š[X][Ûˆ\Ș[Y [‚ˆ -ˆÙIÛXÚÈ]\[™�[ˆHÙ][š[X][Û‹‚ˆ -‹ÂˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÑÑU -Hˆ ‰ˆ -Ý\ˆHš[™Ù[�Ú\™JXÝ[™×Ù[�]KXÝ[™×Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž‹TWÒUSK^Y\—Ý\ÝÜXÚØX›JJJH‚ˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆÙ]ÙÙ][™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÙÙ]ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑÑU  -N‚ˆ Ê‚ˆ -ˆ][H˜]XÚÜȈÛÛXÝ܈ÈXZÙH]ˆ -ˆX\ÞHÈØÜš\XÝ[ÛœÈÛˆ][HXÚÈ\ ˆˆ -‹Âˆ×Ú][WÜØÜš\ -XÝ[™×Ù[�]KÝ\ŠN‚ˆYš[™Ú][JÝ\ŠNˆÛÝÈ[™[šØÚXÚΈB‚ˆ ËÈ\ÙHÝ[[YHÈÚ\™ÙH[™ [[Ý™BˆXÝ[™×Ù[�]KOœÝ[[YHHÝ[YNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ‚ˆYŠXÝ[™×Ù[�]KO�ÙX\[� ‰‚ˆXÝ[™×Ù[�]KO�ÙX\[� O›[Ù[]KœÝX�\HOHÕP•TWÔ“Ò‘PÕSH ‰‚ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÕ“ÕÐUPÒÊH -BˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕ“ÕÐUPÒË -NˆÛÝÈ[™[šØÚXÚΈBˆ[ÙHYŠ\™›Ü›WØ]ÚZ[Š -JBˆˆYŠÛؘ[ÜØ[\WÛ\Ý œ[˜Ú�H  ‰ˆXÝ[™×Ù[�]KO˜]XÚÚ[™ÈOHUPÒÒS‘×Ó“Ó‘JBˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý œ[˜Ú  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆÛÝÈ[™[šØÚXÚΈB‚ˆBˆˆ Ê‚ˆ -ˆ�[\[™ÈÛÛ�Ü› ‚ˆ -‹Â‚ˆYŠXÝ[™×Ü^Y\‹Oœ^ZÙ^\È ˆ“Q×Ò•ST ‰ˆ›Ý[˜Z\ŠBˆˆ ËÈYYZ[˜Z\ŠXÝ[™×Ù[�]JHÛÈ^Y\œÈØ[‰Ý�[\Ú[ˆ˜[[™È[�ÈÛ\ˆXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H‘“Q×Ò•ST‚ˆ Ê‚ˆ -ˆˆ^\ÈÛÛ�›Û ‚ˆ -‚ˆ -ˆYˆ^Y\ˆØ[ˆ[š]X[^™HHˆ�[\ˆ -ˆ[™\܈ÝÛˆÙ^\È\™HXÝ]™HÙIÛˆ -ˆÙ]HÜ�[ÛˆÙˆZ\ˆ�[\ÜYY\ˆ -ˆHˆ™[ØÚ]KˆÙIÛ\ÙH\Ș[YH\ˆ -ˆHˆ\˜[Y]\ˆ›Üˆ[ÝۜݙX[Bˆ -ˆ�[\�[˜Ý[ÛˆØ[Ë‚ˆ -‹Â‚ˆYˆ -XÝ[™×Ù[�]KO›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÖ—ÒS’UPS -BˆˆYˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ[š]X[Ú�[\Ý™[ØÚ]WÞˆH XXÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY -ˆ �NˆBˆ[ÙHYˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ[š]X[Ú�[\Ý™[ØÚ]WÞˆHXÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY -ˆ �NˆBˆBˆ[ÙBˆˆ[š]X[Ú�[\Ý™[ØÚ]WÞˆH ŒˆB‚ˆYŠXÝ[™×Ù[�]KOœ�[›š[™ÊBˆˆ ËÔÛYBˆYŠ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•S”ÓQJJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘Nˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÔ•S”ÓQK -NˆÛÝÈ[™[šØÚXÚΈH‚ˆ ʈ�[\[™È[ÝÙYÈ -‹ÂˆYˆ -JXÝ[™×Ù[�]KO›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÑTÐP“JJBˆˆYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WÔ•S’•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\ZYÚ XÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY -ˆXÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\\Ý [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WÔ•S’•ST -NˆBˆ[ÙHYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WѓԕÐT‘•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\ZYÚ XÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY -ˆXÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\\Ý [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WѓԕÐT‘•ST -NˆBˆ[ÙHYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WÒ•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\ZYÚ XÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY -ˆXÝ[™×Ù[�]KO›[Ù[]Kœ�[š�[\\Ý [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WÒ•ST -NˆBˆBˆBˆ[ÙBˆˆ ËÔÛYBˆYŠ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÔÓQJJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ø]XÚ×Ü›ØÎˆÙ]Ø]XÚÚ[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆXÝ[™×Ù[�]KO˜ÛÛX›ÜÝ\ÌHH ˆXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘Nˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÔÓQK -NˆÛÝÈ[™[šØÚXÚΈB‚ˆ ʈ�[\[™È[ÝÙYÈ -‹ÂˆYˆ -JXÝ[™×Ù[�]KO›[Ù[]K˜Z\—ØÛÛ�›Û ˆRT—ÐÓÓ•“ÓÒ•STÑTÐP“JJBˆ‚ˆ Ê‚ˆ -ˆ[™HY� ÜšYÚ\™XÝ[ÛˆÛÛ[X[™ ‚ˆ -‚ˆ -ˆYˆY�܈šYÚÙ^H\ÈXÝ]™Kˆ -ˆ[ˆÝÚ]Ú˜XÚ[™ÈXØÛÜ™[™ÛH[™ˆ -ˆÛÛ�[�YHÛˆÈ[Ýš[™È�[\ÙÚXË‚ˆ -‚ˆ -ˆÝ\�Ú\ÙH\™›Ü›HH�[\Ú]›Âˆ -ˆÜš^›Û�[™[ØÚ]K‚ˆ -‹Â‚ˆYˆ -JXÝ[™×Ü^Y\‹OšÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -JH ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÒ•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kš�[\ZYÚ  [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WÒ•ST -NˆÛÝÈ[™[šØÚXÚΈBˆ[ÙHYˆ - -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[ÙHYˆ - -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆB‚ˆ Ê‚ˆ -ˆÜš^›Û�[[Ýš[™È�[\ ‚ˆ -‹Â‚ˆYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WѓԕÐT‘•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kš�[\ZYÚ XÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WѓԕÐT‘•ST -NˆBˆ[ÙHYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WÒ•ST -JBˆˆžZ�[\ -XÝ[™×Ù[�]KO›[Ù[]Kš�[\ZYÚ XÝ[™×Ù[�]KO›[Ù[]Kš�[\ÜYY [š]X[Ú�[\Ý™[ØÚ]WÞ‹S’WÒ•ST -NˆBˆBˆBˆ™]\›ŽÂˆB‚ˆ Ê‚ˆ -ˆ�[ˆÝÜ ‚ˆ -‹Âˆ�[—ÝžWÜ�[œÝÜÜ^Y\ŠXÝ[™×Ù[�]KXÝ[™×Ü^Y\ŠN‚ˆ Ê‚ˆ -‹Â‚ˆYŠVQT—ÓRS—ÖˆOHVQT—ÓPVÖˆ ‰ˆXÝ[™×Ù[�]KO™XÚÚ[™ÈOHPÒ×Ó“Ó‘JBˆˆ ËÈ[Ü™HÙˆH]›Ü›H™Y[ˆYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘UT -BˆÈˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÕT -H ‰ˆXÝ[™×Ù[�]KOœ�[›š[™ÈOH•S—ÔÕUWÓ“Ó‘JBˆˆXÝ[ÛˆHPÕSÓ—ÕTˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH XXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž È ŽÈ ËÈ\ÙY›Üˆ\[š[X][Û‚ˆBˆ[ÙHYŠXÝ[™×Ù[�]KOœ�[›š[™È ˆ•S—ÔÕUWÔÕT•Ö -BˆˆXÝ[ÛˆHPÕSÓ—Ô•SŽÂ‚ˆYˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ•S—ÐÓÓ‘’Q×ÖÔ’QÒÑTÒÑ’VQˆ ‰ˆJXÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ•S—ÐÓÓ‘’Q×Ö—ÕTÑS�P“Q -JHˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ŒˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH XXÝ[™×Ù[�]KO›[Ù[]Kœ�[œÜYY È ŽÈ ËÈ[Ý™\È\]H˜\Ý\ˆ˜]H�[›š[™ÂˆBˆBˆ[ÙBˆˆXÝ[ÛˆHPÕSÓ—ÕÐSΈXÝ[™×Ù[�]KO�™[ØÚ]KžˆH XXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž È ŽÂˆBˆBˆ[ÙHYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆÈˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÑÕÓŠH ‰ˆXÝ[™×Ù[�]KOœ�[›š[™ÈOH•S—ÔÕUWÓ“Ó‘JBˆˆXÝ[ÛˆHPÕSÓ—ÑÕӎˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆHXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž È ŽÈ ËÈ\ÙY›ÜˆÝÛˆ[š[X][Û‚ˆBˆ[ÙHYŠXÝ[™×Ù[�]KOœ�[›š[™È ˆ•S—ÔÕUWÔÕT•Ö -BˆˆXÝ[ÛˆHPÕSÓ—Ô•SŽÂ‚ˆYˆ -XÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ•S—ÐÓÓ‘’Q×ÖÔ’QÒÑTÒÑ’VQˆ ‰ˆJXÝ[™×Ù[�]KO›[Ù[]Kœ�[—ØÛÛ™šY×Ù›YÜÈ ˆ•S—ÐÓÓ‘’Q×Ö—ÑÕÓ—ÑS�P“Q -JHˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ŒˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆHXÝ[™×Ù[�]KO›[Ù[]Kœ�[œÜYY È ŽÈ ËÈ[Ý™\È\]H˜\Ý\ˆ˜]H�[›š[™ÂˆBˆBˆ[ÙBˆˆXÝ[ÛˆHPÕSÓ—ÕÐSΈXÝ[™×Ù[�]KO�™[ØÚ]KžˆHXÝ[™×Ù[�]KO›[Ù[]KœÜYY ž È ŽÂˆBˆBˆ[ÙHYŠJXÝ[™×Ü^Y\‹OšÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓŠJJBˆˆXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆBˆBˆ[ÙHYŠXÝ[™×Ù[�]KO™XÚÚ[™ÈOHPÒ×Ó“Ó‘H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÑPÒÊH ‰ˆXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓˆ ‰ˆ›Ý[˜Z\ŠBˆˆžYXÚÊXÝ[™×Ù[�]JNˆÛÝÈ[™[šØÚXÚΈB‚ˆYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• ‰ˆXÝ[™×Ù[�]KO™XÚÚ[™ÈOHPÒ×Ó“Ó‘JBˆˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -Bˆˆ ËØXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NÈ ËÈ]Z]È�[›š[™ÈYˆ^Y\ˆÚ[™Ù\È\™XÝ[Û‚ˆYŠXÝ[™×Ù[�]KO›[Ù[]K�\›™[^H ‰ˆXXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO�\›�[YHHÝ[YH -ÈXÝ[™×Ù[�]KO›[Ù[]K�\›™[^NˆBˆ[ÙHYŠXÝ[™×Ù[�]KO�\›�[YH ‰ˆÝ[YH�HXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO�\›�[YHH ˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÕT“ŠJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ý\›ŽÂˆÙ]Ý\›š[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕT“‹ -NˆÛÝÈ[™[šØÚXÚΈBˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆ[ÙHYŠXXÝ[™×Ù[�]KO›[Ù[]K�\›™[^H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÕT“ŠJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ý\›ŽÂˆÙ]Ý\›š[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕT“‹ -NˆÛÝÈ[™[šØÚXÚΈBˆ[ÙHYŠXXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�\›�[YHH ˆB‚ˆYŠXÝ[™×Ù[�]KOœ�[›š[™ÊBˆˆXÝ[ÛˆHPÕSÓ—Ô•SŽÂˆXÝ[™×Ù[�]KO�™[ØÚ]KžH XXÝ[™×Ù[�]KO›[Ù[]Kœ�[œÜYYÈ ËÈYˆ�[›š[™Ë^Y\ˆ[Ý™\È]H˜\Ý\ˆ˜]BˆBˆ[ÙHYŠXÝ[ÛˆOHPÕSÓ—ÕT ‰ˆXÝ[ÛˆOHPÕSÓ—ÑÕÓŠBˆˆXÝ[ÛˆHPÕSÓ—ÕÐSΈXÝ[™×Ù[�]KO�™[ØÚ]KžH XXÝ[™×Ù[�]KO›[Ù[]KœÜYY žˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�™[ØÚ]KžH XXÝ[™×Ù[�]KO›[Ù[]KœÜYY žˆBˆBˆ[ÙHYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ ‰ˆXÝ[™×Ù[�]KO™XÚÚ[™ÈOHPÒ×Ó“Ó‘JBˆˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -Bˆˆ ËØXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NÈ ËÈ]Z]È�[›š[™ÈYˆ^Y\ˆÚ[™Ù\È\™XÝ[Û‚ˆYŠXÝ[™×Ù[�]KO›[Ù[]K�\›™[^H ‰ˆXXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO�\›�[YHHÝ[YH -ÈXÝ[™×Ù[�]KO›[Ù[]K�\›™[^NˆBˆ[ÙHYŠXÝ[™×Ù[�]KO�\›�[YH ‰ˆÝ[YH�HXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO�\›�[YHH ˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÕT“ŠJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ý\›ŽÂˆÙ]Ý\›š[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕT“‹ -NˆÛÝÈ[™[šØÚXÚΈBˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆ[ÙHYŠXXÝ[™×Ù[�]KO›[Ù[]K�\›™[^H ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÕT“ŠJBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ý\›ŽÂˆÙ]Ý\›š[™ÊXÝ[™×Ù[�]JNˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO�™[ØÚ]KžˆH ˆ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕT“‹ -NˆÛÝÈ[™[šØÚXÚΈBˆ[ÙHYŠXXÝ[™×Ù[�]KO�\›�[YJBˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�\›�[YHH ˆB‚ˆYŠXÝ[™×Ù[�]KOœ�[›š[™ÊBˆˆXÝ[ÛˆHPÕSÓ—Ô•SŽÂˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO›[Ù[]Kœ�[œÜYYÈ ËÈYˆ�[›š[™Ë^Y\ˆ[Ý™\È]H˜\Ý\ˆ˜]BˆBˆ[ÙHYŠXÝ[ÛˆOHPÕSÓ—ÕT ‰ˆXÝ[ÛˆOHPÕSÓ—ÑÕÓŠBˆˆXÝ[ÛˆHPÕSÓ—ÕÐSΈXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO›[Ù[]KœÜYY žˆBˆ[ÙBˆˆXÝ[™×Ù[�]KO�™[ØÚ]KžHXÝ[™×Ù[�]KO›[Ù[]KœÜYY žˆBˆBˆ[ÙHYŠJ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• -Hˆ -XÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JH -Bˆˆ ËØXÝ[™×Ù[�]KOœ�[›š[™ÈH•S—ÔÕUWÓ“Ó‘NÈ ËÈ^Y\ˆ]ÛÈÙˆY� ÜšYÚ[™ÛÈ]Z]È�[›š[™ÂˆXÝ[™×Ù[�]KO�™[ØÚ]KžH ˆXÝ[™×Ù[�]KO�\›�[YHH ˆB‚ˆYŠ -Ý\ˆHš[™Ù[�Ú\™JXÝ[™×Ù[�]KXÝ[™×Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž‹TWÒUSK^Y\—Ý\ÝÝÝXÚ -JH -Bˆˆ×Ú][WÜØÜš\ -XÝ[™×Ù[�]KÝ\ŠNˆYš[™Ú][JÝ\ŠNÈ ËÈYY�[˜Ý[ÛˆÈÛX[ˆÛÙH\Hš]ˆB‚ˆYˆ -XÝ[™×Ù[�]KO™XÚÚ[™È ˆPÒ×ÐPÕU‘JBˆˆYˆ -JXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠJBˆˆžYXÚÜš\ÙJXÝ[™×Ù[�]JNˆÛÝÈ[™[šØÚXÚΈBˆYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘SQ• -BˆˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—ÓQ•ˆBˆBˆ[ÙHYŠXÝ[™×Ü^Y\‹OšÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -BˆˆYŠXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -BˆˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒˆBˆBˆBˆYˆ -XÝ[™×Ù[�]KO™XÚÚ[™ÊBˆˆÛÝÈ[™[šØÚXÚΈB‚‚ˆ ËÕÚ]H˜YÛÛŽˆ™\\™H›ÜˆY[™È[š[X][ۜˋ‹‚ˆYŠXÝ[ÛŠBˆˆXÝ[™×Ù[�]KO�ZÙXXÝ[ÛˆH•SˆXÝ[™×Ù[�]KOšY[™ÈHQS‘×Ô‘TT‘QˆB‚ˆÝÚ]Ú -XÝ[ÛŠBˆˆØ\ÙHPÕSÓ—ÕÐS΂ˆ ËȘXÚÈØ[È™X]\™BˆYŠ]™[ ‰ˆ˜[Y[š[JXÝ[™×Ù[�]KS’WÐ�PÒÕÐSÊJH‚ˆYŠXÝ[™×Ù[�]KO›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÔ’QÒ]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÔ’QÒ -H‚ˆ˜XÚ×ÝØ[ÈHXXÝ[™×Ù[�]KO™\™XÝ[ێ‚ˆH[ÙHYŠXÝ[™×Ù[�]KO›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓQ•]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓQ• -H‚ˆ˜XÚ×ÝØ[ÈHXÝ[™×Ù[�]KO™\™XÝ[ێ‚ˆH[ÙHYŠ -XÝ[™×Ù[�]KO›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S -H ‰ˆ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -H ‰ˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -H‚ˆ˜XÚ×ÝØ[ÈH�YN‚ˆH[ÙHYŠ -XÝ[™×Ù[�]KO›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S -H ‰ˆ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÔ’QÒ -H ‰ˆXÝ[™×Ù[�]KO™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -H‚ˆ˜XÚ×ÝØ[ÈH�YN‚ˆH[ÙHYŠXÝ[™×Ù[�]KO�\›�[YH ‰ˆXÝ[™×Ù[�]KO›[Ù[]K�\›™[^JH‚ˆ˜XÚ×ÝØ[ÈH�YNˆB‚ˆYŠ˜XÚ×ÝØ[ÊHˆÛÛ[[ؘۗXÚÝØ[ר[š[JXÝ[™×Ù[�]JNȈH[ÙHˆÛÛ[[Û—ÝØ[ר[š[JXÝ[™×Ù[�]JNˆBˆˆH[ÙHˆÛÛ[[Û—ÝØ[ר[š[JXÝ[™×Ù[�]JNȈBˆœ™XZΈØ\ÙHPÕSÓ—ÕT‚ˆÛÛ[[Û—Ý\Ø[š[JXÝ[™×Ù[�]JNÈ ËÙ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÕT  -NÈ ËÈÙ]È\[š[X][ÛˆYˆ^\݈œ™XZΈØ\ÙHPÕSÓ—ÑÕÓŽ‚ˆÛÛ[[Û—ÙÝÛ—Ø[š[JXÝ[™×Ù[�]JNÈ ËÙ[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÑÕÓ‹ -NÈ ËÈÙ]ÈÝÛˆ[š[X][ÛˆYˆ^\݈œ™XZΈØ\ÙHPÕSÓ—Ô•SŽ‚ˆYˆ -˜[Y[š[JXÝ[™×Ù[�]KS’WÐ�PÒÔ•SŠJBˆˆYˆ -\×Ú[—ؘXÚÜ�[ŠXÝ[™×Ù[�]JJH[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÐ�PÒÔ•S‹ -Nˆ[ÙH[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÔ•S‹ -NˆBˆ[ÙH[�ÜÙ]Ø[š[JXÝ[™×Ù[�]KS’WÔ•S‹ -NÈ ËÈÙ]È�[ˆ[š[X][ÛˆYˆ^\Ý‚ˆœ™XZΈY˜][‚‚ˆYŠXÝ[™×Ù[�]KOšY[™ÊBˆˆÛÛ[[Û—ÚYWØ[š[JXÝ[™×Ù[�]JNˆBˆœ™XZΈB‚‚™[™[šØÚXÚ΂ˆ ËÚ[œÙ\�ÚXÚÈ\™Bˆ™]\›ŽÂŸB‚š[�\×Ú[—ؘXÚÜ�[Š[�]JˆÙ[ŠBžÂˆYˆ - - -Ù[‹O›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÔ’QÒ]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÔ’QÒ -H ‰ˆÙ[‹O�™[ØÚ]Kž -Hˆ - -Ù[‹O›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓQ•]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓQ• -H ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Hˆ - - -Ù[‹O›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S -H ‰ˆ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÔ’QÒ -JH ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Hˆ - - -Ù[‹O›[Ù[]K™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S]™[ O™˜XÚ[™ÈOH�PÒS‘×ÐQ•TÕÓU‘S -H ‰ˆ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -JH ‰ˆÙ[‹O�™[ØÚ]Kžˆ -Bˆ -H™]\›ˆ Nˆ[ÙH™]\›ˆ ŸB‚‹ËØ[[[ÈÛÝ[�ÛÙ\ÈÝÛ‚�›ÚYÝX�˜XÝÜÚÝ - -BžÂˆYŠÙ[‹O�ÙX\[� ‰ˆÙ[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØÛÝ[� -BˆˆÙ[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØÛÝ[� KN‚ˆ Ê‚ˆ -ˆÝ]Ùˆ\Ù\ÏÈ›ÜHÙX\Û‹‚ˆ -‹Â‚ˆYŠ\Ù[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�\ÙWØÛÝ[� -BˆˆÙ[‹O�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜרÛÝ[�H ˆ›ÜÙX\ÛŠÙ[‹ -NˆBˆBŸB‚‚�›ÚY›ÜÙX\ÛŠ[�]JˆXÝ[™×Ù[�]K[�›YÊBžÂˆ[�Ø[H ˆ[�]H -›Ý\ˆH•Sˆ[�]JˆÙX\Û—Ù[�]HH•Sˆ×ÝÙX\ÛŠˆÙX\Û—Ü›Ü\�Y\ÈH•S‚‚KËÈYˆÙH[™XYH]™HHÙX\Û‹ÙIÛ™YYÈ\ØØ\™] ‚ˆYŠXÝ[™×Ù[�]KO�ÙX\[� -Bˆˆ Ê‚ˆ -ˆ[\Ú[�\œÈÈÙ[‰ÜÈÙX\Ûˆ[�]H[™Hˆ -ˆÙX\Ûˆ[�]IÜÈ[Ù[]HÙX\Ûˆ›Ü\�Y\Ȉ -ˆ[�ÈØØ[˜\šXX›\ˈ\È\È�\ݛ܈XZ\Ù\ˆˆ -ˆ™XY[™ÈÝۜݙX[K‚ˆ -‹Â‚ˆÙX\Û—Ù[�]HHXÝ[™×Ù[�]KO�ÙX\[�ˆÙX\Û—Ü›Ü\�Y\ÈH ˜XÝ[™×Ù[�]KO�ÙX\[� O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\΂‚BKËÈ Œ NKLKLŽH H›ÝÝ\™HX›Ý]\ÈÙÚXˈ]\X\œÈ]Û›H\HÚÝ‚BKËÈÙX\ÛœÈ܈ÙX\ÛœÈÚ]ÚÝ[[[È\™H›ÜY ˆ[ž][™È[ÙH\ÈÚ[\H\ØØ\™Y ‚‚BKËÈ™YYÈ]˜[X]H[ÙX\ÛˆÙÚXÈÈÙ]HÛÜšÙ›ÝË‚ˆYŠÙX\Û—Ü›Ü\�Y\ËO�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÓSRUQÕTÑH -JÙX\Û—Ü›Ü\�Y\ËO�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÓSRUQÕTÑJH ‰ˆÙX\Û—Ü›Ü\�Y\ËO�\ÙWØÛÝ[� -JBˆÈ‚BBKËÈYˆH›YÈ\È ˆ܈™[ÝËÙHÝX�˜XÝH›YÉÜ‚BBKËȘ[YHœ›ÛHÙX\ÛˆÛÝ[�\‹‚‚BBZYŠ›YÈ ŠBˆˆÙX\Û—Ü›Ü\�Y\ËO›ÜÜרÛÝ[� OH›YΈBˆ‚BBKËÈÙIÜ™HÛÚ[™ÈÈ\ÙHÝ\ˆÝÛˆÜÚ][Ûˆ›ÜˆHÙX\Û‹‚ˆÙX\Û—Ù[�]KO™\™XÝ[ÛˆHXÝ[™×Ù[�]KO™\™XÝ[ێˆÙX\Û—Ù[�]KOœÜÚ][Û‹žˆHXÝ[™×Ù[�]KOœÜÚ][Û‹žŽÂˆÙX\Û—Ù[�]KOœÜÚ][Û‹žHXÝ[™×Ù[�]KOœÜÚ][Û‹žˆÙX\Û—Ù[�]KOœÜÚ][Û‹žHHXÝ[™×Ù[�]KOœÜÚ][Û‹žN‚‚BBKËÈÙ][žHØ[È[™]›Ü›\Ë‚ˆÝ\ˆHÚXÚ×Ü]›Ü›JÙX\Û—Ù[�]KOœÜÚ][Û‹ž ÙX\Û—Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]JNˆØ[HÚXÚÝØ[Ú[™^ -ÙX\Û—Ù[�]KOœÜÚ][Û‹ž ÙX\Û—Ù[�]KOœÜÚ][Û‹žŠN‚‚BBKËÈXÙHÛ�ÈØ[܈]›Ü›K‚ˆYŠÝ\ˆ ‰ˆÝ\ˆOHÙX\Û—Ù[�]JBˆˆÙX\Û—Ù[�]KO˜˜\ÙH -ÏHÝ\‹OœÜÚ][Û‹žH -ÈÝ\‹O˜[š[X][Û‹Oœ]›Ü›VÛÝ\‹O˜[š[\Ü×VÔU“Ô“WÒRQÒNˆBˆ[ÙHYŠØ[�H -BˆˆÙX\Û—Ù[�]KO˜˜\ÙH -ÏH]™[ O�Ø[ÖÝØ[KšZYÚˆB‚‚BBKËÈ\ÙHHÙX\Û‰ÜÈ‘TÔUÓˆ܈ÔUÓˆ[š[X][ÛœÈYˆ]˜Z[X›KÝ\�Ú\ÙB‚BBKËÈÛÈšYÚÈYK‚ˆYŠ˜[Y[š[JÙX\Û—Ù[�]KS’WÔ‘TÔUÓŠJBˆˆ[�ÜÙ]Ø[š[JÙX\Û—Ù[�]KS’WÔ‘TÔUÓ‹ JNˆBˆ[ÙHYŠ˜[Y[š[JÙX\Û—Ù[�]KS’WÔÔUÓŠJBˆˆ[�ÜÙ]Ø[š[JÙX\Û—Ù[�]KS’WÔÔUÓ‹ JNˆBˆ[ÙBˆˆYŠ˜[Y[š[JÙX\Û—Ù[�]KS’WÒQJJH[�ÜÙ]Ø[š[JÙX\Û—Ù[�]KS’WÒQK JNˆB‚‚BBKËÈYˆHÙX\Û‰ÜÈÛÝ[�\ˆ\È\]Y [ˆÙX\Ûˆ\ÈÜݛ܈ÛÛÙ ‚‚BBKËÈYˆ]\È[ˆ˜[š[X[‹[ˆÙH\HH[š[X[�[›š[™È]Ø^HÙÚXË‚‚BBKËÈÝ\�Ú\ÙHHÙX\Ûˆ›[šÜÈÝ] ‚ˆYŠ]ÙX\Û—Ü›Ü\�Y\ËO›ÜÜרÛÝ[� -BˆˆYŠJXÝ[™×Ù[�]KO›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—ÜÝ]H ˆÑPTÓ—ÔÕUWÐS’SPS -JBˆˆÙX\Û—Ù[�]KO˜›[šÈH NˆÙX\Û—Ù[�]KO�ZÙXXÝ[ÛˆHÛÛ[[Û—ÛYNˆBˆ[ÙBˆˆÙX\Û—Ù[�]KO›[Ù[]K�\HHTWÓ“Ó‘NˆÙX\Û—Ù[�]KO�[šÈH�[˜[š[X[ˆBˆBˆÙX\Û—Ù[�]KO›™^[šÈHÝ[YH -È NˆB‚‚BKËÈÛX\ˆÝ\ˆ˜XÚÚ[™È˜\šXX›H]ÙY\ÈHÙX\Ûˆ[�]HÚ[�\‹‚ˆXÝ[™×Ù[�]KO�ÙX\[�H•SˆB‚‚KËÈ›YÈ ˆYX[œÈÙIÜ™H›Ø˜X›HÙ][™ÈHÙX\Ûˆ\™XÝH -^ˆÙ]ÙX\ÛˆÛÛ[X[™ -Kˆ‚KËÈ[ˆ]Ø\ÙHÙHÛ‰ÝÛÜœžHX›Ý]HÙX\Ûˆ[�]Kˆ�\ÝÝÚ]ÚÝ\œÙ[™\ÈÝ™\‚‚KËÈÈHÙX\Ûˆ[Ù[ ‚ˆYŠ›YÈ ŠBˆˆYŠXÝ[™×Ù[�]KO›[Ù[]K�\H ˆTWÔVQTŠBˆˆYŠ^Y\–ØXÝ[™×Ù[�]KOœ^Y\š[™^K�ÙX\�[JBˆˆÙ]ÝÙX\ÛŠXÝ[™×Ù[�]K^Y\–ØXÝ[™×Ù[�]KOœ^Y\š[™^K�ÙX\�[K -NˆBˆ[ÙBˆˆÙ]ÝÙX\ÛŠXÝ[™×Ù[�]K]™[ OœÙ]ÙX\  -NˆBˆBˆ[ÙBˆˆÙ]ÝÙX\ÛŠXÝ[™×Ù[�]K  -NˆBˆB‚‚KËÈ[Ù[Ý™\œšYKˆYˆ\È\ÈÜ[]Y ÙH\ÙH]Ș[YB‚KËÈÈØØ]HH[Ù[žH[™^[™™]™\�È][œÝXY‚KËÈÙˆHY˜][[Ù[Ú[ˆHÙX\Ûˆ\ÈÜÝ ‚ˆYŠXÝ[™×Ù[�]KO›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜ×Ú[™^OHSÑSÒS‘VÓ“Ó‘JBˆˆÙ]ÝÙX\ÛŠXÝ[™×Ù[�]KXÝ[™×Ù[�]KO›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜ×Ú[™^  -NˆBŸB‚‚š[�^Y\—ÝZÙY[XYÙJ[�]Jˆ\™Ù]Ù[�]K[�]Jˆ]XÚÚ[™×Ù[�]Kר]XÚʈ]XÚ×ÛØš™XÝ [�˜[Ù›YËÛÛœÝ×ÙY™[œÙJˆY™[œÙWÛØš™XÝ -BžÂˆר]XÚÈ]ÈH -˜]XÚ×ÛØš™X݈ ËÜš[�Š™[XYÙYžNˆ É\ÉÈ Yˆ‹Ý\‹O›˜[YK]XÚËO˜]XÚ×ٛܘÙJN‚‚KËÈܘ]\È - L LŒ ŒJH›ÝÈHš[™š[š]HX[ÚX]ˆÚ[ÚXÚÈH[XYÙHÛÝ\˜ÙK]Ú[]›ÚYÛÛYHœÜXÚX[ˆ[XYÙHÛÝ\˜Ù\‚‚KËÈ[XYÙHÛÛY\Èœ›ÛHH›Ü›X[ÛÝ\˜ÙO‚XÛۜݛÛۛܛX[Ù[XYÙHH -Z\ר]XÚ×Ý\WÜÜXÚX[ -]˘]XÚ×Ý\JJHÈ�YHˆ˜[ÙN‚ˆYŠ -Ûؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÒPSÐPÕU‘H ‰ˆ›Ü›X[Ù[XYÙJBˆ -]™[ O››Ú\�OHSPQÑWÑ”“ÓWÑS‘SVWÓÑ‘ˆ ‰ˆ -]XÚÚ[™×Ù[�]KO›[Ù[]K�\H ˆTWÑS‘SVJJJBˆˆ]˘]XÚ×ٛܘÙHH ˆBˆˆ™]\›ˆÛÛ[[Û—ÝZÙY[XYÙJ\™Ù]Ù[�]K]XÚÚ[™×Ù[�]K ˜]˘[Ù›YËY™[œÙWÛØš™XÝ -NŸB‚‹ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË‚‹ËÈØ[YÚ[ˆ^Y\ˆ™KY[�\œÈHØ[YK‚‹ËÈ›Ü[[™[ZY\ÈVÑT›ÜˆH[šÙY Ùœ›Þ™[ˆÛ™\Ë‚�›ÚY›ÜØ[Ù[™[ZY\Ê -BžÂˆ[�Nˆ[�]H -�ÙX\Ù[ˆHÙ[ŽÂˆר]XÚÈ]XÚÈH[\X]XÚ΂ˆ›ÜŠHH ÈH[�ÛX^ÈJÊÊBˆˆYŠ[�Û\ÝÚWKO™^\ÝÈ ‰‚ˆ[�Û\ÝÚWKO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ  ‰‚ˆ -[�Û\ÝÚWKO›[Ù[]K�\H ˆTWÑS‘SVJH ‰‚ˆY[�Û\ÝÚWKO›ÝÛ™\ˆ ‰ˆ ËÈÛ‰ÝØ[�ÈÛ›ØÚÈÝÛˆH›Ú™XÝ[BˆY[�Û\ÝÚWKO™œ›Þ™[ˆ ‰ˆ ËÈÛ‰ÝØ[�È[™œ™Y^™HHœ›Þ™[ˆ[™[^BˆJ[�Û\ÝÚWKO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×Ó“×ÓSÕ‘JH ‰‚ˆJ[�Û\ÝÚWKO›[Ù[]KœZ[—ØÛÛ™šY×Ù›YÜÈ ˆRS—ÐÓÓ‘’Q×Ñ�SÑTÐP“JH ‰‚ˆ˜[Y[š[J[�Û\ÝÚWKS’WÑ�S -H -Bˆˆ[�Û\ÝÚWKO˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘Nˆ[�Û\ÝÚWKOœ›Ú™XÝ[HH“TÕÓ“Ó‘Nˆ[�Û\ÝÚWKO�ZÙXXÝ[ÛˆHÛÛ[[Û—Ù˜[ËËÙ[™[^WÙ˜[ˆ[�Û\ÝÚWKO™[XYÙWÛÛ—Û[™[™Ë˜]XÚ×ٛܘÙHH ˆ[�Û\ÝÚWKO™[XYÙWÛÛ—Û[™[™Ë˜]XÚ×Ý\HHU×Ó“Ó‘NˆÙ[ˆH[�Û\ÝÚWNˆ[�Ý[›[šÊÙ[ŠNˆ[�Û\ÝÚWKO�™[ØÚ]KžH -Ù[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -HÈ - LKŒŠHˆ KŒŽÂˆˆYŠ[�Û\ÝÚWKO›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë›ÜÜרÛÛ™][Ûˆ ˆÑPTÓ—ÓÔÔ×ÐÓÓ‘USÓ—ÔÕQÑJBˆˆ›ÜÙX\ÛŠÙ[‹ JNˆB‚ˆÜÜÊ[�Û\ÝÚWK ‹�H -Ș[™Š JJNˆ[�Û\ÝÚWKOšÛ›ØÚÙÝÛ˜ÛÝ[�H[�Û\ÝÚWKO›[Ù[]KšÛ›ØÚÙÝÛ˜ÛÝ[�‚ˆ[�Û\ÝÚWKOšÛ›ØÚÙÝÛ�[YHH ‚ˆÙ]Ù˜[ -[�Û\ÝÚWKÙ[‹ ˜]XÚË JNˆBˆBˆÙ[ˆHÙX\Ù[ŽÂŸB‚‚‚‹ÊˆØ[YÚ[ˆ›ÜÜÈY\ˈ -‹Â�›ÚYÚ[Ø[Ù[™[ZY\Ê -BžÂˆ[�Nˆר]XÚÈ]XÚÈH[\X]XÚΈ[�]H -�\Ù[ˆH•S‚‚X]XÚ˘]XÚ×Ý\HHU×ГÔÔ×ÑPU‚X]XÚË™›ÜˆHY˜][Û[Ù[ٛ܎‚ˆ ʈˆ -ˆÝۜݙX[H[XYÙH�[˜Ý[ÛœÈ\ÙHˆ -ˆÙ[ˆÛؘ[ ÛÈÙHÜ[]H]Ú]ˆ -ˆ[�]HÝ\œÛ܈[ˆXXÚ]\˜][ÛˆÙˆˆ -ˆÛÜ™[ÝˈÙY\Ý\œ™[�˜[YH\™Hˆ -ˆÛÈÙHØ[ˆ™\ÝÜ™HÚ[ˆÛÜ\Èš[š\ÚY ˆˆ -‹Âˆ\Ù[ˆHÙ[ŽÂ‚ˆ ʈˆ -ˆ]™\žH˜[Y[™[^H\HÚ]HZÙY[XYÙBˆ -ˆ�[˜Ý[ÛˆZÙ\È]ÈÝ\œ™[�X[[ˆ[XYÙK‚ˆ -‹Âˆ›ÜŠHH ÈH[�ÛX^ÈJÊÊBˆˆYŠ[�Û\ÝÚWKO™^\݈ ‰ˆ[�Û\ÝÚWKO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ ˆ ‰ˆ -[�Û\ÝÚWKO›[Ù[]K�\H ˆTWÑS‘SVJBˆ ‰ˆ[�Û\ÝÚWKO�ZÙY[XYÙJBˆˆÙ[ˆH[�Û\ÝÚWN‚ˆ]XÚ˘]XÚ×ٛܘÙHHÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆÙ[‹O�ZÙY[XYÙJÙ[‹Ù[‹ ˜]XÚË Ù[‹O™Y™[œÙJNˆBˆB‚ˆÙ[ˆH\Ù[ŽÈŸB‚‚‚�›ÚYÛX\�Ø›ÛXŠ[�]H -™Kר]XÚÈ -˜]XÚÊHÈ ËÈ™]ÈY]ٛ܈ÛX\�›ÛXœÂ‚ˆ[�K]H ˆ[�]H -�\Ù[ˆH•S‚ˆ\Ù[ˆHÙ[ŽÂˆ›ÜŠHH ÈH[�ÛX^ÈJÊÊH‚ˆYŠ[�Û\ÝÚWKO™^\݈ ‰ˆ[�Û\ÝÚWHOHBˆ ‰ˆ[�Û\ÝÚWKO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ ˆ ‰ˆ˜XÝ[Û—ØÚXÚ×Ú\×ÚÜÝ[JK[�Û\ÝÚWJJHˆˆÙ[ˆH[�Û\ÝÚWNˆ]H NÈ Ëț܈›ØÛÜÝ YˆH›ÛXˆÙ\Û‰Ý] ]ÛÛ‰ÝÛÜÝ[™\™ÞB‚ˆ\Ý] ˜]XÚÈH]XÚΈ\Ý] ˜]XÚÙ\ˆHNˆ\Ý] ˜ÛÛ\Ú[Û—Ø]XÚÈH•Sˆ\Ý] ˜ÛÛ™š\›HH Nˆ\Ý] ™]XÝØ›ÙHH•Sˆ\Ý] ™]XÝØÛÛ\Ú[Û—Ø]XÚÈH•Sˆ\Ý] ™]XÝØÛÛ\Ú[Û—Ø›ÙHH•Sˆ\Ý] œÜÚ][Û‹žHÙ[‹OœÜÚ][Û‹žˆ\Ý] œÜÚ][Û‹žHHÙ[‹OœÜÚ][Û‹žNˆ\Ý] œÜÚ][Û‹žˆHÙ[‹OœÜÚ][Û‹žŽÂˆ\Ý] �\™Ù]HÙ[ŽÂ‚ˆYŠÙ[‹O�ZÙY[XYÙJH‚ˆÛÛœÝ×ÙY™[œÙJˆY™[œÙWÛØš™XÝHY™[œÙWÙš[™ØÝ\œ™[�ÛØš™XÝ -Ù[‹•S ]XÚËO˜]XÚ×Ý\JNˆˆ ËØ]XÚ˘]XÚ×Ù›ÜHÙ[‹O›[Ù[]KšÛ›ØÚÙÝÛ˜ÛÝ[� -ÌNˆÙ[‹O�ZÙY[XYÙJÙ[‹K]XÚË Y™[œÙWÛØš™XÝ -NˆˆH[ÙHˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OH]XÚËO˜]XÚ×ٛܘÙNˆˆYŠÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H -HˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÔÓPT•“ÓPŠNˆBˆB‚ˆÜ]Û—Ø]XÚ×Ù›\Ú -Ù[‹]XÚË]XÚËO™›\Ú ›[Ù[Ú] Ù[‹O›[Ù[]K™›\Ú ›[Ù[Ø›ØÚÊNˆˆBˆB‚ˆYŠ›ØÛÜÝ ‰ˆ] ‰ˆÛX\�›ÛX™\ŠHÈ ËÈÛ‰Ý\ÙHK™XØ]\ÙH\ÈØ[ˆ™H[ˆ][KX›ÛX‚ˆÙ[ˆHÛX\�›ÛX™\ŽÂˆˆYŠÚXÚ×Ù[™\™ÞJS‘T‘ÖWÕTWÓT S’WÔÔPÒPS -JHˆÙ[‹O™[™\™ÞWÜÝ]K›\ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈H[ÙHˆÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OHÙ[‹O›[Ù[]K˜[š[X][Û–ÐS’WÔÔPÒPSKO™[™\™ÞWØÛÜÝ ˜ÛÜ݈BˆˆB‚ˆÙ[ˆH\Ù[ŽÂ‚ŸB‚‚‹ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË‚�›ÚY[ž][™×ÝØ[Ê -BžÂˆYŠÙ[‹OœÜÚ][Û‹žY˜[˜Ù^ H Ù[‹OœÜÚ][Û‹žˆY˜[˜Ù^ -È -šY[Û[Ù\Ëš™\È -È -JBˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÕÐS×ÓÕUÓїГÕS‘ÊNˆ™]\›ŽÂˆBˆ ËÜÙ[‹OœÜÚ][Û‹ž -ÏHÙ[‹O�™[ØÚ]KžŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ NKLL‹LN‹Ë‹ËÈ\HÛÛ܈Ù]Y�\ÝY[�È[�]KÜÜÚX›B‹ËȘ\ÙYÛˆH\™[� ÛÝÛ™\ˆ\[™[™ÈÛˆÛÛ܈Ù]‹ËÈY�\ÝY[�Ù][™Ë‚�›ÚY\WØÛÛÜ—ÜÙ]ØY�\Ý -[�]Jˆ[� [�]Jˆ\™[� WØÛÛÜ—ØY�\ÝY�\ÝY[� -BžÂ‚Z[�HH È ËÈÛÜÝ\œÛÜ‹‚‚‚KËÈ\HÛÛ܈Ù][™Ë‚‚\ÝÚ]Ú -Y�\ÝY[� -B‚^‚YY˜][‚‚B‚BKËÈ\ÙHY�\ÝY[�˜[YH\ÈÛÛ܈Ù][™^ ‚‚BY[�ÜÙ]ØÛÛÝ\›X\ -[� Y�\ÝY[� -N‚BXœ™XZ΂B‚XØ\ÙHÓÓÔ”ÑUÐQ•TÕÓ“Ó‘N‚‚BB‚BKËÈÈ›Ý[™Ë‚BB‚BXœ™XZ΂B‚XØ\ÙHÓÓÔ”ÑUÐQ•TÕÔT‘S•ÒS‘V‚‚‚BKËÈØØ]H\™[� ÜÈÝ\œ™[�ÛÛ܈Ù][™^ ˆ[‚‚BKËÈÙ]Ý\ˆÛÛ܈Ù]žH][™^ ‚‚‚BY›Üˆ -HH ÈH\™[� O›[Ù[]K›X\×ÛØYYÈJÊÊB‚B^‚BBZYˆ -\™[� O˜ÛÛÝ\›X\OH\™[� O›[Ù[]K˜ÛÛÝ\›X\ÚWJB‚BB^‚BBBY[�ÜÙ]ØÛÛÝ\›X\ -[� JN‚BBBXœ™XZ΂BB_B‚B_B‚BXœ™XZ΂‚XØ\ÙHÓÓÔ”ÑUÐQ•TÕÔT‘S•ÕP“N‚‚BB‚BKËÈ\ÙH\™[� ÜÈÛÛ܈X›K‚‚‚BY[� O˜ÛÛÝ\›X\H\™[� O˜ÛÛÝ\›X\‚BXœ™XZ΂‚_BŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Š‚Šˆ Œ NKLL‹LŒ‚ŠˆÛÜHH˜XÝ[ÛˆÙ][™ÜÈœ›ÛHHŠˆÛÝ\˜ÙH[�]K‚Š‹È�›ÚY˜XÝ[Û—ØÛÜWØ[ -[�]Jˆ\Ý [�]JˆÛÝ\˜ÙJBžÂˆ˜XÝ[Û—ØÛÜWÙ]J ™\Ý O›[Ù[]K™˜XÝ[Û‹ œÛÝ\˜ÙKO™˜XÝ[ÛŠNˆ˜XÝ[Û—ØÛÜWÙ]J ™\Ý O™˜XÝ[Û‹ œÛÝ\˜ÙKO™˜XÝ[ÛŠNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËL ‹LŽŠˆŠˆÛÜH˜XÝ[Ûˆ›Ü\�Y\ÈYˆŠˆÛÛ™][ÛœÈ\ÜË‚Š‹Â�›ÚY˜XÝ[Û—ØÛÜWÙ]J×Ù˜XÝ[ÛŠˆ\Ý ×Ù˜XÝ[ÛŠˆÛÝ\˜ÙJH‚ˆ Ê‚ˆ -ˆH\Ý[˜][Ûˆ\È[Ø^\ÈH˜XÝ[Û‚ˆ -ˆØš™XÝ ™YØ\™\ÜÈÙˆÚXÚ˜XÝ[Û‚ˆ -ˆ›Ü\�Y\È]X[YžH›ÜˆÛÜZ[™Ë‚ˆ -‹Âˆ\Ý O›Øš™XÝÝ\HHÐ’‘PÕÕTWÑ�PÕSӎ‚ˆ Ê‚ˆ -ˆ˜XÝ[ۈܛÝ\ˈÛ›HÛÜHYˆÛÝ\˜ÙH܈ˆ -ˆ\Ý[˜][Ûˆ\ÈH˜[YH[™Ù\ț݈ -ˆ]™HH›ÈÛÜH›YË‚ˆ -ˆˆ -ˆIÙ˜]\ˆ™HX›HÈÛÜH[\H˜[Y\Ȉ -ˆ\ÈÙ[ �]]™\]Z\™\È[œÙ\�[™Èˆ -ˆY˜][˜[Y\È[ˆÙ]™\˜[Y™™\›�ˆ -ˆØØ][ÛœÈ\›Ý[™HÛÙKˆ™]\ˆˆ -ˆÙY\]\™H[™XZ[�Z[˜X›Kˆˆ -‹Â‚ˆYˆ -ÛÝ\˜ÙKO™[XYÙWÙ\™XÝOH�PÕSÓ—ÑÔ“ÕTÓ“Ó‘Hˆ ‰ˆJÛÝ\˜ÙKO™[XYÙWÙ\™XÝ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O™[XYÙWÙ\™XÝ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJJHˆ\Ý O™[XYÙWÙ\™XÝHÛÝ\˜ÙKO™[XYÙWÙ\™XÝȈB‚ˆYˆ -ÛÝ\˜ÙKO™[XYÙWÚ[™\™XÝOH�PÕSÓ—ÑÔ“ÕTÓ“Ó‘Hˆ ‰ˆJÛÝ\˜ÙKO™[XYÙWÚ[™\™XÝ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O™[XYÙWÚ[™\™XÝ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJJHˆ\Ý O™[XYÙWÚ[™\™XÝHÛÝ\˜ÙKO™[XYÙWÚ[™\™XÝȈBˆˆYˆ -ÛÝ\˜ÙKOšÜÝ[HOH�PÕSÓ—ÑÔ“ÕTÓ“Ó‘Hˆ ‰ˆJÛÝ\˜ÙKOšÜÝ[H ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJHˆ ‰ˆJ\Ý OšÜÝ[H ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJJHˆ\Ý OšÜÝ[HHÛÝ\˜ÙKOšÜÝ[NȈBˆˆYˆ -ÛÝ\˜ÙKO›Y[X™\ˆOH�PÕSÓ—ÑÔ“ÕTÓ“Ó‘Hˆ ‰ˆJÛÝ\˜ÙKO›Y[X™\ˆ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O›Y[X™\ˆ ˆ�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔJJHˆ\Ý O›Y[X™\ˆHÛÝ\˜ÙKO›Y[X™\ŽÈˆB‚ˆ Ê‚ˆ -ˆ\\ˈØ[YH�[H\ȘXÝ[ۈܛÝ\Ë‚ˆ -‹Â‚ˆYˆ -ÛÝ\˜ÙKO�\WÙ[XYÙWÙ\™XÝOHTWÕS‘PÓT‘Qˆ ‰ˆJÛÝ\˜ÙKO�\WÙ[XYÙWÙ\™XÝ ˆTWÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O�\WÙ[XYÙWÙ\™XÝ ˆTWÓ“×ÐÓÔJJHˆˆ\Ý O�\WÙ[XYÙWÙ\™XÝHÛÝ\˜ÙKO�\WÙ[XYÙWÙ\™XÝȈB‚ˆYˆ -ÛÝ\˜ÙKO�\WÙ[XYÙWÚ[™\™XÝOHTWÕS‘PÓT‘Qˆ ‰ˆJÛÝ\˜ÙKO�\WÙ[XYÙWÚ[™\™XÝ ˆTWÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O�\WÙ[XYÙWÚ[™\™XÝ ˆTWÓ“×ÐÓÔJJHˆ\Ý O�\WÙ[XYÙWÚ[™\™XÝHÛÝ\˜ÙKO�\WÙ[XYÙWÚ[™\™XÝȈB‚ˆYˆ -ÛÝ\˜ÙKO�\WÚÜÝ[HOHTWÕS‘PÓT‘Qˆ ‰ˆJÛÝ\˜ÙKO�\WÚÜÝ[H ˆTWÓ“×ÐÓÔJHˆ ‰ˆJ\Ý O�\WÚÜÝ[H ˆTWÓ“×ÐÓÔJJHˆ\Ý O�\WÚÜÝ[HHÛÝ\˜ÙKO�\WÚÜÝ[NȈHŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ Œ‹L KL�Š‚Šˆ™XYH^\™Ý[Y[�›Üˆ[Ù[ÛÜH›YŠˆ[™Ý]]\›ÜšX]HÛÛœÝ[� ‚Š‹Â™˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ݘXÝ[Û—ÙÙ]Ù›Y×Ùœ›ÛWÜÝš[™ÊÛÛœÝÚ\Šˆ˜[YJH‚ˆÛÛœÝÝ�X݈ÛÛœÝÚ\Šˆ^Û˜[YNˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×Ý›Y΂ˆH›Y×ÛÛÚÝ\ÝX›V×HHˆÈ››Û™H‹�PÕSÓ—ÑÔ“ÕTÓ“Ó‘HKˆÈ˜[‹�PÕSÓ—ÑÔ“ÕTÐSÓ“Ô“PSKˆȘ[ ‹�PÕSÓ—ÑÔ“ÕTÐSÓ“Ô“PSÌKˆȘ[ H‹�PÕSÓ—ÑÔ“ÕTÐSÓ“Ô“PSÌHKˆÈ›™]]˜[‹�PÕSÓ—ÑÔ“ÕTÓ‘UU�SKˆÈ››×ØÛÜH‹�PÕSÓ—ÑÔ“ÕTÓ“×ÐÓÔHKˆÈœ^Y\—Ý™\œÙ\È‹�PÕSÓ—ÑÔ“ÕTÔVQT—Õ‘T”ÑTÈKˆÈ�\WÙ^Û\Ú]™H‹�PÕSÓ—ÑÔ“ÕTÕTWÑVÓTÒU‘HKˆÈ�\WÚ[˜Û\Ú]™H‹�PÕSÓ—ÑÔ“ÕTÕTWÒS�ÓTÒU‘HKˆÈ˜H‹�PÕSÓ—ÑÔ“ÕTÐHKˆÈ˜ˆ‹�PÕSÓ—ÑÔ“ÕTЈKˆÈ˜È‹�PÕSÓ—ÑÔ“ÕTÐÈKˆÈ™‹�PÕSÓ—ÑÔ“ÕTÑKˆÈ™H‹�PÕSÓ—ÑÔ“ÕTÑHKˆÈ™ˆ‹�PÕSÓ—ÑÔ“ÕTшKˆÈ™È‹�PÕSÓ—ÑÔ“ÕTÑÈKˆÈš‹�PÕSÓ—ÑÔ“ÕTÒKˆÈšH‹�PÕSÓ—ÑÔ“ÕTÒHKˆÈšˆ‹�PÕSÓ—ÑÔ“ÕTÒˆKˆÈšÈ‹�PÕSÓ—ÑÔ“ÕTÒÈKˆÈ›‹�PÕSÓ—ÑÔ“ÕTÓKˆÈ›H‹�PÕSÓ—ÑÔ“ÕTÓHKˆÈ›ˆ‹�PÕSÓ—ÑÔ“ÕTÓˆKˆÈ›È‹�PÕSÓ—ÑÔ“ÕTÓÈKˆÈœ‹�PÕSÓ—ÑÔ“ÕTÔKˆÈœH‹�PÕSÓ—ÑÔ“ÕTÔHKˆÈœˆ‹�PÕSÓ—ÑÔ“ÕTÔˆKˆÈœÈ‹�PÕSÓ—ÑÔ“ÕTÔÈKˆÈ�‹�PÕSÓ—ÑÔ“ÕTÕKˆÈ�H‹�PÕSÓ—ÑÔ“ÕTÕHKˆÈ�ˆ‹�PÕSÓ—ÑÔ“ÕTÕˆKˆÈ�È‹�PÕSÓ—ÑÔ“ÕTÕÈKˆÈž‹�PÕSÓ—ÑÔ“ÕTÖKˆÈžH‹�PÕSÓ—ÑÔ“ÕTÖHKˆÈžˆ‹�PÕSÓ—ÑÔ“ÕTÖˆKˆÈ˜LH‹�PÕSÓ—ÑÔ“ÕTÐLHKˆÈ˜ŒH‹�PÕSÓ—ÑÔ“ÕTÐŒHKˆÈ˜ÌH‹�PÕSÓ—ÑÔ“ÕTÐÌHKˆÈ™ H‹�PÕSÓ—ÑÔ“ÕTÑ HKˆÈ™LH‹�PÕSÓ—ÑÔ“ÕTÑLHKˆÈ™ŒH‹�PÕSÓ—ÑÔ“ÕTÑŒHKˆÈ™ÌH‹�PÕSÓ—ÑÔ“ÕTÑÌHKˆÈš H‹�PÕSÓ—ÑÔ“ÕTÒ HKˆÈšLH‹�PÕSÓ—ÑÔ“ÕTÒLHKˆÈšŒH‹�PÕSÓ—ÑÔ“ÕTÒŒHKˆÈšÌH‹�PÕSÓ—ÑÔ“ÕTÒÌHKˆÈ› H‹�PÕSÓ—ÑÔ“ÕTÓ HKˆÈ›LH‹�PÕSÓ—ÑÔ“ÕTÓLHKˆÈ›ŒH‹�PÕSÓ—ÑÔ“ÕTÓŒHKˆÈ›ÌH‹�PÕSÓ—ÑÔ“ÕTÓÌHKˆÈœ H‹�PÕSÓ—ÑÔ“ÕTÔ HKˆÈœLH‹�PÕSÓ—ÑÔ“ÕTÔLHKˆÈœŒH‹�PÕSÓ—ÑÔ“ÕTÔŒHKˆÈœÌH‹�PÕSÓ—ÑÔ“ÕTÔÌHKˆÈ� H‹�PÕSÓ—ÑÔ“ÕTÕ HKˆÈ�LH‹�PÕSÓ—ÑÔ“ÕTÕLHKˆÈ�ŒH‹�PÕSÓ—ÑÔ“ÕTÕŒHKˆÈ�ÌH‹�PÕSÓ—ÑÔ“ÕTÕÌHKˆÈž H‹�PÕSÓ—ÑÔ“ÕTÖ HKˆÈžLH‹�PÕSÓ—ÑÔ“ÕTÖLHKˆÈžŒH‹�PÕSÓ—ÑÔ“ÕTÖŒHBˆN‚ˆÛÛœÝÚ^™WÝ\ÝØÛÝ[�HÚ^™[ÙŠ›Y×ÛÛÚÝ\ÝX›JH ÈÚ^™[ÙŠ -™›Y×ÛÛÚÝ\ÝX›JN‚ˆ›Üˆ -Ú^™WÝHH ÈH\ÝØÛÝ[�ÈJÊÊHˆYˆ -ÝšXÛ\ -˜[YK›Y×ÛÛÚÝ\ÝX›VÚWK�^Û˜[YJHOH -Hˆ™]\›ˆ›Y×ÛÛÚÝ\ÝX›VÚWK™›YΈBˆB‚ˆ Ê‚ˆ -ˆÛÝ[‰Ýš[™HX]Ú[ˆHÛÚÝ\ˆ -ˆX›KˆÙ[™[\�ÈÙÈ[™™]\›‚ˆ -ˆ›Û™H›YË‚ˆ -‹Â‚ˆš[�Š——ˆ[šÛ›ÝÛˆ˜XÝ[Ûˆ - \ÊKˆˆ‹˜[YJNˆ™]\›ˆ�PÕSÓ—ÑÔ“ÕTÓ“Ó‘NŸB‚‹Ê‚‹HØ\ÚÙ^K[[Ûˆ‹‚‹H Œ �‹L LLB‹B‹H™XY˜XÝ[Ûˆ\™Ý[Y[�È\™XÝHœ›ÛHHÛÝ\˜ÙH[™H[™ˆÛÛXš[™HZ\ˆÛÜœ™\ÜÛ™[™ÈÜ›Ý\›YÜË‚Š‹Â™˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ݘXÝ[Û—ÙÙ]Ù›YÜ×Ùœ›ÛWØÛÛ[X[™Û[™JˆÛÛœÝÚ\ŠˆÛÛ[X[™Û[™BŠBžÂˆÛÛœÝÚ\Šˆ˜[YNˆרÛÛ[X[™Ø\™Ý[Y[�Ü™XY\ˆ™XY\ŽÂˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×Ý™\Ý[H�PÕSÓ—ÑÔ“ÕTÓ“Ó‘N‚ˆÛÛ[X[™Ø\™Ý[Y[�Ü™XY\—Ú[š]X[^™J œ™XY\‹ÛÛ[X[™Û[™K JN‚ˆÚ[JÛÛ[X[™Ø\™Ý[Y[�Ü™XY\—Û™^ - œ™XY\‹ �˜[YJJHˆ™\Ý[H˜XÝ[Û—ÙÙ]Ù›Y×Ùœ›ÛWÜÝš[™Ê˜[YJNˆB‚ˆ™]\›ˆ™\Ý[ŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËL ‹L�‚Š‚Šˆ™]\›ˆ�YHYˆXÝ[™È[�]HØ[ˆŠˆ]\™Ù][�]HÚ]]XÚÜË‚Š‹Â˜›ÛÛ˜XÝ[Û—ØÚXÚרØ[—Ù[XYÙJ[�]JˆXÝ[™×Ù[�]K[�]Jˆ\™Ù]Ù[�]KÛۜݛÛÛ[™\™XÝ -H‚ˆWÙ[�]WÝ\HXÝ[™×Ý\NˆWÙ[�]WÝ\H\™Ù]Ý\Nˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ÝXÝ[™×Ù˜XÝ[ێˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ÝXÝ[™×Ù˜XÝ[Û—Ùš[\™Yˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×Ý\™Ù]Ù˜XÝ[ێ‚ˆYˆ -XXÝ[™×Ù[�]H]\™Ù]Ù[�]JHˆ™]\›ˆ˜[ÙNˆB‚ˆ Ê‚ˆ -ˆÙHÚ[\ÙHY™™\™[�XÝ[™È˜XÝ[Ûˆ›Ü\�Bˆ -ˆ[™\HYˆH[™\™XÝ›YÈ\ÈÙ] ‚ˆ -‹Â‚ˆYˆ -[™\™XÝ -HˆXÝ[™×Ù˜XÝ[ÛˆHXÝ[™×Ù[�]KO™˜XÝ[Û‹™[XYÙWÚ[™\™X݈XÝ[™×Ý\HHXÝ[™×Ù[�]KO™˜XÝ[Û‹�\WÙ[XYÙWÚ[™\™X݈ˆH[ÙHˆXÝ[™×Ù˜XÝ[ÛˆHXÝ[™×Ù[�]KO™˜XÝ[Û‹™[XYÙWÙ\™X݈XÝ[™×Ý\HHXÝ[™×Ù[�]KO™˜XÝ[Û‹�\WÙ[XYÙWÙ\™X݈B‚ˆXÝ[™×Ù˜XÝ[Û—Ùš[\™YHXÝ[™×Ù˜XÝ[ێ‚ˆ Ê‚ˆ -ˆ™[[Ý™HÜXÚX[\\Ë‚ˆ -‹Â‚ˆXÝ[™×Ù˜XÝ[Û—Ùš[\™Y �H‘�PÕSÓ—ÑÔ“ÕTÓ“×ÐÒPÒΈXÝ[™×Ý\H �H•TWÓ“×ÐÒPÒ΂ˆ Ê‚ˆ -ˆÚXÚÈ^Y\ˆ[�\˜XÝ[Û‹‚ˆ -‹Â‚ˆYˆ -˜XÝ[Û—ØÚXÚ×Ü^Y\—Ý™\œÙ\ÊXÝ[™×Ù[�]K\™Ù]Ù[�]KXÝ[™×Ù˜XÝ[ÛŠJHˆ™]\›ˆ˜[ÙNˆB‚ˆ\™Ù]Ý\HH\™Ù]Ù[�]KO›[Ù[]K�\N‚ˆ Ê‚ˆ -ˆYˆÛ™HÙˆHXÝ[™È˜XÝ[ÛœÈ\ÈBˆ -ˆ\H^Û\Ú]™HÜ›Ý\ [ˆÙHÛ›Bˆ -ˆÚXÚÈXÝ[™È\\Ȝˈ\™Ù]\Bˆ -ˆ[™YۛܙHÝ\ˆ˜XÝ[ۜ˂ˆ -‹Â‚ˆYˆ -XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÑVÓTÒU‘JHˆˆYˆ -XÝ[™×Ý\H ˆ\™Ù]Ý\JHˆ™]\›ˆ�YNˆH[ÙHˆ™]\›ˆ˜[ÙNˆBˆB‚ˆ Ê‚ˆ -ˆ›ÝÈÙHÛÛ\\™HÝ\ˆXÝ[™È˜XÝ[ÛŠÊHÈBˆ -ˆ˜XÝ[ÛŠÊH\™Ù]\ÈHY[X™\ˆÙˆ›Üˆ[žHX]Ú ‚ˆ -‹Â‚ˆ\™Ù]Ù˜XÝ[ÛˆH\™Ù]Ù[�]KO™˜XÝ[Û‹›Y[X™\ŽÂ‚ˆYˆ -XÝ[™×Ù˜XÝ[Û—Ùš[\™Y ˆ\™Ù]Ù˜XÝ[ÛŠHˆ Ê‚ˆ -ˆYˆÛ™HÙˆHXÝ[™È˜XÝ[ÛœÈ\ˆ -ˆHYH[˜Û\Ú[™ÈÜ›Ý\ [ˆÙBˆ -ˆ[ÛÈÚXÚÈXÝ[™È\\ÈœÈBˆ -ˆ\™Ù] ÜÈ\K‚ˆ -‹Â‚ˆYˆ -XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÒS�ÓTÒU‘JHˆˆYˆ -XÝ[™×Ý\H ˆ\™Ù]Ý\JHˆ™]\›ˆ�YNˆˆH[ÙHˆ™]\›ˆ˜[ÙNˆBˆB‚ˆ™]\›ˆ�YNˆB‚ˆ™]\›ˆ˜[ÙNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËL ‹L�‚ŠˆŠˆ™]\›ˆ�YHYˆXÝ[™È[�]H\ŠˆÜÝ[HÝØ\™\™Ù][�]K‚Š‹Âš[�˜XÝ[Û—ØÚXÚ×Ú\×ÚÜÝ[J[�]JˆXÝ[™×Ù[�]K[�]Jˆ\™Ù]Ù[�]JBžÂˆ ËÜš[�Š——ˆ˜XÝ[Û—ØÚXÚ×Ú\×ÚÜÝ[J \  \ -H‹XÝ[™×Ù[�]K\™Ù]Ù[�]JN‚ˆWÙ[�]WÝ\HXÝ[™×Ý\NˆWÙ[�]WÝ\H\™Ù]Ý\Nˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ÝXÝ[™×Ù˜XÝ[ێˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×Ýš[\™YÙ˜XÝ[ێˆ˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×Ý\™Ù]Ù˜XÝ[ێ‚ˆYˆ -XXÝ[™×Ù[�]H]\™Ù]Ù[�]JBˆˆ™]\›ˆ ˆB‚ˆ ËÜš[�Š——XÝ[™×Ù[�]KO›˜[YNˆ \Ë\™Ù]Ù[�]KO›˜[YNˆ \È‹XÝ[™×Ù[�]KO›˜[YK\™Ù]Ù[�]KO›˜[YJN‚ˆXÝ[™×Ù˜XÝ[ÛˆHXÝ[™×Ù[�]KO™˜XÝ[Û‹šÜÝ[Nˆš[\™YÙ˜XÝ[ÛˆHXÝ[™×Ù˜XÝ[ێˆXÝ[™×Ý\HHXÝ[™×Ù[�]KO™˜XÝ[Û‹�\WÚÜÝ[Nˆ\™Ù]Ý\HH\™Ù]Ù[�]KO›[Ù[]K�\N‚ˆ ËÜš[�Š——XÝ[™×Ù˜XÝ[ÛŽˆ Y‹XÝ[™×Ù˜XÝ[ÛŠNˆ ËÜš[�Š——XÝ[™×Ý\Nˆ Y‹XÝ[™×Ý\JNˆ ËÜš[�Š——\™Ù]Ý\Nˆ Y‹\™Ù]Ý\JN‚ˆ Ê‚ˆ -ˆ™[[Ý™HÜXÚX[\\Ë‚ˆ -‹Â‚ˆš[\™YÙ˜XÝ[Ûˆ �H‘�PÕSÓ—ÑÔ“ÕTÓ“×ÐÒPÒΈXÝ[™×Ý\H �H•TWÓ“×ÐÒPÒ΂ˆ ËÜš[�Š——XÝ[™×Ù˜XÝ[ÛŽˆ Y‹XÝ[™×Ù˜XÝ[ÛŠNˆ ËÜš[�Š——XÝ[™×Ý\Nˆ Y‹XÝ[™×Ý\JN‚ˆ Ê‚ˆ -ˆÚXÚÈ^Y\ˆ[�\˜XÝ[Û‹ˆˆ -‹Â‚ˆYˆ -˜XÝ[Û—ØÚXÚ×Ü^Y\—Ý™\œÙ\ÊXÝ[™×Ù[�]K\™Ù]Ù[�]KXÝ[™×Ù˜XÝ[ÛŠJBˆˆ ËÜš[�Š——^Y\ˆœËˆ™]\›ˆ ŠN‚ˆ™]\›ˆ ˆB‚ˆ Ê‚ˆ -ˆYˆÛ™HÙˆHXÝ[™È˜XÝ[ÛœÈ\ÈBˆ -ˆ\H^Û\Ú]™HÜ›Ý\ [ˆÙHÛ›Bˆ -ˆÚXÚÈXÝ[™È\\Ȝˈ\™Ù]\Bˆ -ˆ[™YۛܙHÝ\ˆ˜XÝ[ۜ˂ˆ -‹Â‚ˆYˆ -XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÑVÓTÒU‘JBˆˆ ËÜš[�Š——XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÑVÓTÒU‘Nˆ Y‹XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÑVÓTÒU‘JN‚ˆYˆ -XÝ[™×Ý\H ˆ\™Ù]Ý\JBˆˆ ËÜš[�Š——XÝ[™×Ý\H ˆ\™Ù]Ý\Nˆ Y -Y\ÊH‹XÝ[™×Ý\H ˆ\™Ù]Ý\JN‚ˆ™]\›ˆ NˆBˆ[ÙBˆˆš[�Š——XÝ[™×Ý\H ˆ\™Ù]Ý\Nˆ Y -›ÊH‹XÝ[™×Ý\H ˆ\™Ù]Ý\JN‚ˆ™]\›ˆ ˆBˆB‚ˆ Ê‚ˆ -ˆ›ÝÈÙHÛÛ\\™HÝ\ˆXÝ[™È˜XÝ[ÛŠÊHÈBˆ -ˆ˜XÝ[ÛŠÊH\™Ù]\ÈHY[X™\ˆÙˆ›Üˆ[žHX]Ú ‚ˆ -‹Â‚ˆ\™Ù]Ù˜XÝ[ÛˆH\™Ù]Ù[�]KO™˜XÝ[Û‹›Y[X™\ŽÂ‚ˆYˆ -š[\™YÙ˜XÝ[Ûˆ ˆ\™Ù]Ù˜XÝ[ÛŠBˆˆ Ê‚ˆ -ˆYˆÛ™HÙˆHXÝ[™È˜XÝ[ÛœÈ\ˆ -ˆHYH[˜Û\Ú[™ÈÜ›Ý\ [ˆÙBˆ -ˆ[ÛÈÚXÚÈXÝ[™È\\ÈœÈBˆ -ˆ\™Ù] ÜÈ\K‚ˆ -‹Â‚ˆYˆ -XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÒS�ÓTÒU‘JBˆˆ ËÜš[�Š——XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÒS�ÓTÒU‘Nˆ Y‹XÝ[™×Ù˜XÝ[Ûˆ ˆ�PÕSÓ—ÑÔ“ÕTÕTWÒS�ÓTÒU‘JN‚ˆYˆ -XÝ[™×Ý\H ˆ\™Ù]Ý\JBˆˆ ËÜš[�Š——XÝ[™×Ý\H ˆ\™Ù]Ý\Nˆ Y -Y\ÊH‹XÝ[™×Ý\H ˆ\™Ù]Ý\JN‚ˆ™]\›ˆ NˆBˆ[ÙBˆˆ ËÜš[�Š——XÝ[™×Ý\H ˆ\™Ù]Ý\Nˆ Y -›ÊH‹XÝ[™×Ý\H ˆ\™Ù]Ý\JN‚ˆ™]\›ˆ ˆBˆB‚ˆ™]\›ˆ NˆB‚ˆ ËÜš[�Š——›ÈX]Ú ˆŠN‚ˆ™]\›ˆ ŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ ŒËL ‹LŽŠˆŠˆ™]\œÈ HYˆ^Y\œÈ\™H›Ý[ÝÙYŠˆœËˆ[�\˜XÝ[ÛˆÛˆHÚ]™[ˆ˜XÝ[ÛˆŠˆ›Ü\�Kˆ^ ˆÜÝ[K\™XÝ[XYÙKŠˆ[™\™XÝ[XYÙK‚Š‹Âš[�˜XÝ[Û—ØÚXÚ×Ü^Y\—Ý™\œÙ\Ê[�]JˆXÝ[™×Ù[�]K[�]Jˆ\™Ù]Ù[�]K˜XÝ[Û—ÙÜ›Ý\ÛX\Ú×ݘXÝ[Û—Ü›Ü\�JBžÂˆYˆ -XXÝ[™×Ù[�]H]\™Ù]Ù[�]JBˆˆ™]\›ˆ ˆB‚ˆ Ê‚ˆ -ˆ›Ý[�]\È]\Ý™H^Y\œË‚ˆ -‹Â‚ˆYˆ -JXÝ[™×Ù[�]KO›[Ù[]K�\H ˆTWÔVQTŠJBˆˆ™]\›ˆ ˆB‚ˆYˆ -J\™Ù]Ù[�]KO›[Ù[]K�\H ˆTWÔVQTŠJBˆˆ™]\›ˆ ˆB‚ˆ Ê‚ˆ -ˆ[ˆ˜XÝ[ۈܛÝ\ÈÝ™\œšYHœšY[™Bˆ -ˆš\™HÙ][™Üψ -‹Â‚ˆYˆ -˜XÝ[Û—Ü›Ü\�H ˆ�PÕSÓ—ÑÔ“ÕTÔVQT—Õ‘T”ÑTÊBˆˆ™]\›ˆ ˆB‚ˆ Ê‚ˆ -ˆYˆ”ˈ[ÙH\›™YÙ™ˆ[ˆÜ[ۜˆ -ˆ܈]™[›Ú]\È[˜X›Y [‚ˆ -ˆÙH\™H™œšY[™HˆÝØ\™\™Ù] ‚ˆ -‹Â‚ˆYˆ -Ø]™Y]K›[ÙJBˆˆ™]\›ˆ NȈB‚ˆYˆ -]™[ ‰ˆ]™[ O››Ú]OHSPQÑWÑ”“ÓWÔVQT—ÓÑ‘ŠBˆˆ™]\›ˆ NˆB‚ˆ™]\›ˆ ŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ NKLL‹LN -™Y˜XÝÜŠBŠ‚ŠˆÜšYÚ[˜[]]܈[šÛ›ÝÛˆ -Z[ÏÊKˆ™Y˜XÝÜ™YÈ™[[Ý™HH]™\‹YÜ›ÝÚ[™È\˜[Y]\ˆ\ÝŠˆ[™ÛÛœÛÛY]H›Ú™XÝ[HÜ]ÛˆÙÚXˈÜ]ÛœÈ[ˆ[�]H[™š\™\È]\ÈH›Ú™XÝ[KˆŠˆ[Ù[\ÙY›ÜˆÜ]Ûˆ\È]\›Z[™YžHHY\˜\˜ÚHÙˆYØXÞH\˜[Y]\œÈ -ÙYH]Z[YŠˆÛÛ[Y[�È[ˆ�[˜Ý[ÛŠK‚Š‚Šˆ™]\›œÈÚ[�\ˆÙˆÜ]Û™Y›Ú™XÝ[K܈•SÛˆ˜Z[ ‚Š‹Â™[�]H -šÛšY™WÜÜ]ÛŠ[�]H -œ\™[� ×ܛڙXÝ[H -œ›Ú™XÝ[JBžÂˆ[�]H -œ›Ú™XÝ[WÙ[�]HH•S‚‚\ר^\×Üš[˜Ú\[Ù›Ø]ÜÚ][ێ‚YWÙ\™XÝ[Ûˆ\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒ‚YWܛڙXÝ[WÜš[YH›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓ“Ó‘N‚B‚KʈYˆ\™IÜțțڙXÝ[H܈\™[�Ù][™Ë^]›Ýˈ -‹Â‚ZYˆ -\›Ú™XÝ[H\\™[� -B‚^‚B\™]\›ˆ•S‚_B‚ˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž‚\ÜÚ][Û‹žHH\™[� OœÜÚ][Û‹žH -țڙXÝ[KOœÜÚ][Û‹žN‚\ÜÚ][Û‹žˆH\™[� OœÜÚ][Û‹žˆ -țڙXÝ[KOœÜÚ][Û‹žŽÂ‚ˆ Ê‚‚Jˆ›ÝÈÙH™YYÈÜ]ÛˆH›Ú™XÝ[H[�]Kˆ\™H\™HX[žH\^˜\™YØXÞH‚JˆY][ÛœÈÈÚY�›ÝYÚ ÛÈÙH™YYÈš[Üš]^™HÚXÚ[Ù[ÈÜ]Û‹ˆ‚Jˆ[ˆÙ[™\˜[ ÙHÛܚȘXÚÈœ›ÛH[ÜÝܘ[�[\ˆÈ[ÜÝÛؘ[ ‚‚J‚‚Jˆœ›ÛHYÚ\ÝÈÝÙ\Ýš[Üš]N‚‚Jˆ‚Jˆ Kˆ›Ú™XÝ[HÛšY™H›Ü\�K‚‚Jˆ ‹ˆ›Ú™XÝ[H›\Ú›Ü\�K‚‚Jˆ ˈ\Ú[™ÈÙX\ÛˆÚ][Ù[›Ú™XÝ›Ü\�K‚‚Jˆ ˆ[Ù[ÛšY™H›Ü\�K‚‚Jˆ Kˆ[Ù[ÚݛțÜ\�K‚‚Jˆ ‹ˆÛؘ[\™ÛÙH[Ù[˜[YK’ÛšY™H‹‚‚Jˆ ˈÛؘ[\™ÛÙH[Ù[˜[YK”ÚÝ‹‚‚J‹ÂˆYˆ -›Ú™XÝ[KOšÛšY™H�H -B‚^ˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S ›Ú™XÝ[KOšÛšY™K•S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÖN‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÔ“Ò—ÒÓ’Q‘N‚_B‚Y[ÙHYˆ -›Ú™XÝ[KO™›\Ú�H -B‚^ˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S ›Ú™XÝ[KO™›\Ú •S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÑ“ÓԎ‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÔÕUSÓ�T–N‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÔ“Ò—Ñ“TÒ‚_B‚Y[ÙHYˆ -\™[� O�ÙX\[� ‰ˆ\™[� O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ�H -B‚^ˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S \™[� O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ •S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÖN‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÕÑPTÓ—Ô“Ò‘PÕSN‚_B‚Y[ÙHYˆ -\™[� O›[Ù[]KšÛšY™H�H -B‚^ˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S \™[� O›[Ù[]KšÛšY™K•S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÖN‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÓSÑSÒÓ’Q‘N‚_B‚Y[ÙHYˆ -\™[� O›[Ù[]KœÚÝ›È�H -B‚^ˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S \™[� O›[Ù[]KœÚݛ˕S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÑ“ÓԎ‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÔÕUSÓ�T–N‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÓSÑSÔÒÕ“΂_B‚Y[ÙB‚^ˆ Ê‚‚BJˆ›È[Ù[[™^\ÈÙ] ÛÈ] ÜȘ[˜XÚÈ‚BJˆHYØXÞH\™ÛÙH[Ù[˜[Y\Ë‚ˆ -‚‚BJˆžH\™ÛÙHšÛšY™Hˆš\œÝ ˆYˆ]˜Z[ËÙIÛžB‚BJˆœÚ݈™^ ‚ˆ -‹Âˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹’ÛšY™H‹SÑSÒS‘VÓ“Ó‘K•S -N‚BB‚BZYˆ -›Ú™XÝ[WÙ[�]JB‚B^ˆ Ê‚‚BBJˆ\™ÛÙHÛšY™HÜ]ÛˆÝXØÙ\ÜÙ�[ ˆX\šÈ\ÈYØXÞHÛšY™B‚BBJˆ[™ÛÛ�[�YK‚ˆ -‹È‚BB\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÑÓÐ�SÒÓ’Q‘N‚B_B‚BY[ÙH‚B^‚BBKʈžHœÚÝ‹ˆ -‹Âˆ›Ú™XÝ[WÙ[�]HHÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹”ÚÝ‹SÑSÒS‘VÓ“Ó‘K•S -N‚‚BB\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÑÓÐ�SÔÒÕ‚B_B‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÖN‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂_B‚‚KʈYˆÙH™]™\ˆÝXØÙ\ÜÙ�[HÜ]Û™YH›Ú™XÝ[H[�]K^] ˆ -‹ÂˆYŠ\›Ú™XÝ[WÙ[�]JBˆˆ™]\›ˆ•SˆB‚ˆ Ê‚ˆ -ˆÙ]™\Ý[Ùˆ\™XÝ[ÛˆY�\ÝY[� ˆÙH™YY\È™Y›Ü™HÙHØ[ˆ[™Bˆ -ˆÜÚ][Ûš[™ÈÛˆ^\Ë‚ˆ -‹Âˆ\™XÝ[ÛˆH\™XÝ[Û—ÙÙ]ØY�\ÝY[�Ü™\Ý[ -›Ú™XÝ[WÙ[�]K\™[� ›Ú™XÝ[KO™\™XÝ[Û—ØY�\Ý -N‚ˆ Ê‚ˆ -ˆ] ÜÈÙ]\HÜ]ÛˆÜÚ][Û‹ˆ™]™\œÙHÚ[‚ˆ -ˆ\™[�˜XÙ\ÈY� ‚ˆ -‚ˆ -ˆ\HY˜][ÜÚ][ÛˆYˆÜ™X]܈Y›ÝÚ]™Bˆ -ˆ\ÈH˜[YK‚ˆ -‹Â‚ˆYˆ -›Ú™XÝ[KOœÜÚ][Û‹žOH“Ò‘PÕSWÓQÐPÖWÐÓÓTUP’SUWÔÔÒUSÓ—Ö -Bˆˆ›Ú™XÝ[KOœÜÚ][Û‹žH“Ò‘PÕSWÑQ�USÔÔÒUSÓ—ÖˆB‚ˆYˆ -\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž -țڙXÝ[KOœÜÚ][Û‹žˆBˆ[ÙBˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž H›Ú™XÝ[KOœÜÚ][Û‹žˆB‚ˆ›Ú™XÝ[WÙ[�]KO™\™XÝ[ÛˆH\™XÝ[ێˆ›Ú™XÝ[WÙ[�]KOœÜÚ][Û‹žHÜÚ][Û‹žˆˆ ʈˆ -ˆ^Y\ˆ›Ú™XÝ[\È\™H[Ø^\È\HœÚÝ‹[›\ÜȈ -ˆ\Ú[™ÈHÝ\œ™[�“Ò‘PÕSH\K‚ˆ -‹ÂˆYˆ -J›Ú™XÝ[WÙ[�]KO›[Ù[]K�\H ˆTWÔ“Ò‘PÕSJJBˆˆYˆ -\™[� O›[Ù[]K�\H ˆTWÔVQTŠBˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K�\HHTWÔÒÕˆBˆ[ÙBˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K�\HH\™[� O›[Ù[]K�\NˆBˆB‚‚Kʈ\H›Ú™XÝ[Hš[YH›YÜˈ -‹Âˆ›Ú™XÝ[WÙ[�]KOœ›Ú™XÝ[WÜš[YHH›Ú™XÝ[WÜš[YN‚ˆ Ê‚‚JˆÛÜHÙ™™[œÙH˜[Y\Èœ›ÛH\™[�Ù™™[œÙHÙ][™ÜÈ‚JˆÈ›Ú™XÝ[H[š]HYˆ™\]Y\ÝY ‚‚J‹ÂˆYˆ -›Ú™XÝ[KO›Ù™™[œÙHOH“Ò‘PÕSWÓÑ‘‘S”ÑWÔT‘S• -B‚^‚B[Y[XÜJ›Ú™XÝ[WÙ[�]KO›Ù™™[œÙK\™[� O›Ù™™[œÙKÚ^™[ÙŠ -œ›Ú™XÝ[WÙ[�]KO›Ù™™[œÙJH -ˆX^Ø]XÚ×Ý\\ÊN‚_B‚‚Kʈ\HÛÛ܈Y�\ÝY[� ˆ -‹Â‚X\WØÛÛÜ—ÜÙ]ØY�\Ý -›Ú™XÝ[WÙ[�]K\™[� ›Ú™XÝ[KO˜ÛÛÜ—ÜÙ]ØY�\Ý -N‚BB‚ˆ Ê‚‚JˆYˆ›È[Ý™K[ˆ[ÜYY\È ˆÝ\�Ú\ÙHÚXÚț܈\ÙHÙ‚‚Jˆ›Ú™XÝ[H™[ØÚ]KˆYˆ^Y\ˆÝ\YY[žH˜[YHÝ\ˆ‚Jˆ[ˆSÑSÔÔQQÓ“Ó‘KÙH\ÙH^Y\‰ÜȘ[YKˆYˆ›Ý ˜[‚Jˆ˜XÚÈÈY˜][˜[Y\ˈ\È\ÈHš]Ý™\˜ÛÛ\XØ]Y �]‚Jˆ[ÝÜÈ^Y\œÈÈÝ\HH ™[ØÚ]H˜[YHÛˆ[žH^\Ë‚‚J‹ÂˆYˆ -›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×Ó“×ÓSÕ‘JB‚^ˆ›Ú™XÝ[WÙ[�]KO›[Ù[]KœÜYY žH ˆ›Ú™XÝ[WÙ[�]KO›[Ù[]KœÜYY žHH ˆ›Ú™XÝ[WÙ[�]KO›[Ù[]KœÜYY žˆH ‚_B‚Y[ÙB‚^ÂB‚BKʈÛÜHÜYY˜[Y\Èœ›ÛH[š[X][Ûˆ›Ú™XÝ[HÙ][™ÜÈÈ[Ù[ ˆ -‹Âˆ›Ú™XÝ[KO�™[ØÚ]HH›Ú™XÝ[WÙ[�]KO›[Ù[]KœÜYY‚_B‚‚KʈÙ]\™Z]š[܈›YÜˈ -‹Âˆ›Ú™XÝ[WÙ[�]KOœÜ]Û�\HHÔUÓ—ÕTWÔ“Ò‘PÕSWÓ“Ô“PSˆ›Ú™XÝ[WÙ[�]KO›ÝÛ™\ˆH\™[�ˆ›Ú™XÝ[WÙ[�]KO››ÙܘXˆH Nˆ›Ú™XÝ[WÙ[�]KO˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘Nˆ›Ú™XÝ[WÙ[�]KO�[šÈHÛÛ[[Û—Ý[šÎˆ›Ú™XÝ[WÙ[�]KO›™^[šÈHÝ[YH -È Nˆ›Ú™XÝ[WÙ[�]KO�ž[[Ý™HH•Sˆ›Ú™XÝ[WÙ[�]KO�ZÙY[XYÙHH\œ›Ý×ÝZÙY[XYÙNˆ›Ú™XÝ[WÙ[�]KO�ZÙXXÝ[ÛˆH•Sˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K˜Z[[Ý™HHRSSÕ‘LWÐT”“ÕΈ›Ú™XÝ[WÙ[�]KOœÜYY][H ŽÂˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K˜ZX]XÚÈHRPUPÒÌWÓ“ÐUPÒΈˆYŠ\›Ú™XÝ[WÙ[�]KO›[Ù[]K›Ù™œØÜ™Y[šÚ[ -Bˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K›Ù™œØÜ™Y[šÚ[H Œ È ËÙY˜][˜[YBˆBBˆ‚KʈÚ[Ù[ˆÚ[ˆÙH] ˆ -‹Â‚ZYˆ -›Ú™XÝ[WÙ[�]KO›[Ù[]Kœ™[[Ý™WØÛÛ™šYÈ ˆ‘SSÕ‘WÐÓÓ‘’Q×ÒU -B‚^ˆ›Ú™XÝ[WÙ[�]KO˜]]ÚÚ[HUUÒÒSÐUPÒ×ÒU‚_B‚Bˆ ʈÚ[Ù[ˆÚ[ˆÙHš[š\Ú[š[X][Û‹ˆ -‹Â‚ZYˆ -›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×Ó“×ÓSÕ‘JB‚^ˆ›Ú™XÝ[WÙ[�]KO˜]]ÚÚ[HUUÒÒSÐS’SPUSÓ—ÐÓÓTUN‚_B‚B‚Kʈ\È\ÈH›Û܈܈›Z[™È›Ú™XÝ[HÈÙ]˜\ÙHXØÛÜ™[™ÛKˆ -‹ÂˆYŠ›Ú™XÝ[WÙ[�]KOœ›Ú™XÝ[WÜš[YH ˆ“Ò‘PÕSWÔ’SQWÐ�TÑWÑ“ÓÔŠBˆˆ›Ú™XÝ[WÙ[�]KO˜˜\ÙHH ˆBˆ[ÙBˆˆ›Ú™XÝ[WÙ[�]KO˜˜\ÙHHÜÚ][Û‹žNˆB‚ˆ Ê‚‚JˆÛÜH˜XÝ[Ûˆ]Hœ›ÛH\™[� ‚‚J‹Â‚ˆ˜XÝ[Û—ØÛÜWØ[ -›Ú™XÝ[WÙ[�]K\™[� -N‚ˆ Ê‚‚JˆYˆ^Y\ˆ[XYÙH\›™YÙ™‹™[[Ý™H^Y\ˆ\Hœ›ÛB‚JˆÜÝ[H -ÛÈÛZ[™È›Ú™XÝ[\ÈX]™H^Y\œÈ[Û™JH[™‚Jˆœ›ÛHØ[™[XYÙK‚ˆ -‹ÂˆYŠ -\™[� O›[Ù[]K�\H ˆTWÔVQTŠH ‰ˆ - -]™[ ‰ˆ]™[ O››Ú]OHSPQÑWÑ”“ÓWÔVQT—ÓÑ‘ŠHØ]™Y]K›[ÙJJBˆˆ›Ú™XÝ[WÙ[�]KO™˜XÝ[Û‹�\WÚÜÝ[H �H•TWÔVQTŽÂˆ›Ú™XÝ[WÙ[�]KO™˜XÝ[Û‹�\WÙ[XYÙWÙ\™XÝ �H•TWÔVQTŽÂˆB‚‚KÊ‚ˆ -ˆÙ]\œ˜Z[ˆ[™[Ý™[Y[�™Z]š[܈›YÜˈYØXÞHˆ -ˆ™Z]š[܈ÛÝ[‰Ý[ÝÈ[Ý™[Y[�[Û™ÈH^\ˆ -ˆÚ]Ý][ˆ\˜Ë�]ÙHØ[‰Ýš^\ÈÚ]Ý]ˆ -ˆœ™XZÚ[™ÈÛÛ\]Xš[]KÛÈÙIÛ™YYHY™™\™[�ˆ -ˆÙ]\\[™[™ÈÛˆÚ]�[˜Ý[ۈܙX]܈\ÙY ‚ˆ -‹Âˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈH -SÕ‘WÐÓÓ‘’Q×Ô“Ò‘PÕSWÐ�TÑWÑQHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÒÓHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÔU“Ô“HSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÕÐSSÕ‘WÐÓÓ‘’Q×Ô“Ò‘PÕSWÕÐSГÕS�ÑJNˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ �H“SÕ‘WÐÓÓ‘’Q×Ó“×ÐQ•TÕÐ�TÑN‚ˆYˆ -›Ú™XÝ[WÙ[�]KOœ›Ú™XÝ[WÜš[YH ˆ“Ò‘PÕSWÔ’SQWÒS’UPSV‘WÓQÐPÖWÔ“Ò‘PÕSWÑ•S�ÕSÓŠBˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÑÔ�U’UNˆBˆ[ÙBˆˆ›Ú™XÝ[WÙ[�]KO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ �H“SÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÑÔ�U’UNˆBˆ‚Kʈ^XÝ]HH›Ú™XÝ[IÜÈÛˆÜ]Ûˆ]™[� ˆ -‹Â‚Y^XÝ]WÛÛœÜ]Û—ÜØÜš\ -›Ú™XÝ[WÙ[�]JN‚B‚\™]\›ˆ›Ú™XÝ[WÙ[�]NŸB‚�›ÚY›ÛX—Ù^ÙJ -BžÂˆYŠÙ[‹O˜[š[X][™ÊBˆˆ™]\›ŽÂˆBˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ГÓP—ÑVÑWÐS’SPUSÓ—ÐÓÓTUJNŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ NKLL‹LŒˆ -™Y˜XÝÜŠBŠ‚ŠˆÜšYÚ[˜[]]܈[šÛ›ÝÛˆ -Z[ÏÊKˆ™Y˜XÝÜ™YÈ™[[Ý™HH]™\‹YÜ›ÝÚ[™È\˜[Y]\ˆ\ÝŠˆ[™ÛÛœÛÛY]H›Ú™XÝ[HÜ]ÛˆÙÚXˈÜ]ÛœÈ[ˆ[�]H[™š\™\È]\ÈH›ÛXˆ›Ú™XÝ[KˆŠˆ[Ù[\ÙY›ÜˆÜ]Ûˆ\È]\›Z[™YžHHY\˜\˜ÚHÙˆYØXÞH\˜[Y]\œÈ -ÙYH]Z[YŠˆÛÛ[Y[�È[ˆ�[˜Ý[ÛŠK‚Š‚Šˆ™]\›œÈÚ[�\ˆÙˆÜ]Û™Y›Ú™XÝ[K܈•SÛˆ˜Z[ ‚Š‹Â™[�]H -˜›ÛX—ÜÜ]ÛŠ[�]H -œ\™[� ×ܛڙXÝ[H -œ›Ú™XÝ[JBžÂ‚Y[�]Jˆ[�H•S‚\ר^\×Üš[˜Ú\[Ù›Ø]ÜÚ][ێ‚YWÙ\™XÝ[Ûˆ\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒ‚YWܛڙXÝ[WÜš[YH›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓ“Ó‘N‚‚KËÈYˆ\™IÜțțڙXÝ[H܈\™[�Ù][™Ë^]›ÝË‚‚ZYˆ -\›Ú™XÝ[H\\™[� -B‚^‚B\™]\›ˆ•S‚_BˆBˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž‚\ÜÚ][Û‹žHH\™[� OœÜÚ][Û‹žH -țڙXÝ[KOœÜÚ][Û‹žN‚\ÜÚ][Û‹žˆH\™[� OœÜÚ][Û‹žˆ -țڙXÝ[KOœÜÚ][Û‹žŽÂ‚ˆ Ê‚‚Jˆ›ÝÈÙH™YYÈÜ]ÛˆH›Ú™XÝ[H[�]Kˆ\™H\™HX[žH\^˜\™YØXÞH‚JˆY][ÛœÈÈÚY�›ÝYÚ ÛÈÙH™YYÈš[Üš]^™HÚXÚ[Ù[ÈÜ]Û‹ˆ‚Jˆ[ˆÙ[™\˜[ ÙHÛܚȘXÚÈœ›ÛH[ÜÝܘ[�[\ˆÈ[ÜÝÛؘ[ ‚‚J‚‚Jˆœ›ÛHYÚ\ÝÈÝÙ\Ýš[Üš]N‚‚Jˆ‚Jˆ Kˆ›Ú™XÝ[H›ÛXˆ›Ü\�K‚‚Jˆ ‹ˆ\Ú[™ÈÙX\ÛˆÚ][Ù[›ÛXˆ›Ü\�K‚‚Jˆ ˈ[Ù[›ÛXˆ›Ü\�K‚‚J‹Â‚ˆYŠ›Ú™XÝ[KO˜›ÛXˆ�H -Bˆˆ[�HÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S ›Ú™XÝ[KO˜›ÛX‹•S -N‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÔ“җГÓPŽÂ‚_B‚Y[ÙHYˆ -Ù[‹O�ÙX\[� ‰ˆÙ[‹O�ÙX\[� O›[Ù[]KœÝX�\HOHÕP•TWÔ“Ò‘PÕSH ‰ˆÙ[‹O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ�H -B‚^‚BY[�HÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S Ù[‹O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ •S -N‚BB‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÕÑPTÓ—Ô“Ò‘PÕSN‚_Bˆ[ÙHYŠÙ[‹O›[Ù[]K˜›ÛXˆ�H -Bˆˆ[�HÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S Ù[‹O›[Ù[]K˜›ÛX‹•S -N‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÓSÑSГÓPŽÂ‚_B‚B‚\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÑ“ÓԎ‚\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂B‚KËÈYˆÙH™]™\ˆÝXØÙ\ÜÙ�[HÜ]Û™YH›Ú™XÝ[H[�]K^] ‚ˆYŠY[� -Bˆˆ™]\›ˆ•SˆB‚ˆ Ê‚ˆ -ˆÙ]™\Ý[Ùˆ\™XÝ[ÛˆY�\ÝY[� ˆÙH™YY\È™Y›Ü™HÙHØ[ˆ[™Bˆ -ˆÜÚ][Ûš[™ÈÛˆ^\Ë‚ˆ -‹Âˆ\™XÝ[ÛˆH\™XÝ[Û—ÙÙ]ØY�\ÝY[�Ü™\Ý[ -[� \™[� ›Ú™XÝ[KO™\™XÝ[Û—ØY�\Ý -N‚ˆ Ê‚ˆ -ˆ] ÜÈÙ]\HÜ]ÛˆÜÚ][Û‹ˆ™]™\œÙHÚ[‚ˆ -ˆ\™[�˜XÙ\ÈY� ‚ˆ -‚ˆ -ˆ\HY˜][ÜÚ][ÛˆYˆÜ™X]܈Y›ÝÚ]™Bˆ -ˆ\ÈH˜[YK‚ˆ -‹Â‚ˆYˆ -›Ú™XÝ[KOœÜÚ][Û‹žOH“Ò‘PÕSWÓQÐPÖWÐÓÓTUP’SUWÔÔÒUSÓ—Ö -Bˆˆ›Ú™XÝ[KOœÜÚ][Û‹žH“Ò‘PÕSWÑQ�USÔÔÒUSÓ—ÖˆB‚ˆYˆ -\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž -țڙXÝ[KOœÜÚ][Û‹žˆBˆ[ÙBˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž H›Ú™XÝ[KOœÜÚ][Û‹žˆB‚ˆ[� O™\™XÝ[ÛˆH\™XÝ[ێˆ[� OœÜÚ][Û‹žHÜÚ][Û‹ž‚‚‚Y[� Oœ›Ú™XÝ[WÜš[YHH›Ú™XÝ[WÜš[YN‚ˆ Ê‚ˆ -ˆYˆ›È[Ý™K[ˆ[ÜYY\È ˆÝ\�Ú\ÙHÚXÚț܈\ÙHÙ‚‚Jˆ›Ú™XÝ[H™[ØÚ]KˆYˆ^Y\ˆÝ\YY[žH˜[YHÝ\ˆ‚Jˆ[ˆSÑSÔÔQQÓ“Ó‘KÙH\ÙH^Y\‰ÜȘ[YKˆYˆ›Ý ˜[‚Jˆ˜XÚÈÈY˜][˜[Y\ˈ\È\ÈHš]Ý™\˜ÛÛ\XØ]Y �]‚Jˆ[ÝÜÈ^Y\œÈÈÝ\HH ™[ØÚ]H˜[YHÛˆ[žH^\Ë‚ˆ -‹Â‚‚ZYˆ -[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×Ó“×ÓSÕ‘JB‚^‚BY[� O›[Ù[]KœÜYY žH ‚BY[� O›[Ù[]KœÜYY žHH ‚BY[� O›[Ù[]KœÜYY žˆH ‚_B‚Y[ÙB‚^ÂBB‚BKËÈÛÜHÜYY˜[Y\Èœ›ÛH[š[X][Ûˆ›Ú™XÝ[HÙ][™ÜÈÈ[Ù[ ‚‚B\›Ú™XÝ[KO�™[ØÚ]KžH[� O›[Ù[]KœÜYY ž‚_B‚ˆ Ê‚‚JˆÜÜÈH›ÛXˆ[�]H[ˆ[ˆ\˜Ë‚‚J‚‚JˆÙHØ[�È[™HYØXÞH™Z]š[܈ -\ÙH›Ú™XÝ[H�[\ZYÚ‚Jˆ›ÜˆHÜÜÈ™[ØÚ]JK[ÝÈ]]܈È\HH ˜[YH›ÜˆH‚Jˆ™[ØÚ]K[™\ÙHHØ[YH™[ØÚ]HÝ�XÝ\™HY[X™\œÈ›Üˆ›ÛXˆ‚Jˆ[™ÛšY™H›Ú™XÝ[\ˈH\Ý\�\È[ˆ^˜HÚ[[™ÙH™XØ]\ÙH‚JˆHY˜][™[ØÚ]H™YYț܈ۚY™H[™›ÛXˆ\™H›ÝÛÛ\]X›Kˆ‚JˆÈ[™H[Ùˆ\È\ÈÙHÚ[Ù]H™[ØÚ]KžHY[X™\ˆÈ‚JˆSÑSÔÔQQÓ“Ó‘HÜXÙšXØ[HÚ[ˆH›ÛXˆ›Ú™XÝ[H\È™\]Y\ÝY‚JˆžHH›ÝÙœ˜[YHÛÛ[X[™܈˜›ÛXˆˆ\Hœ›ÛHYØXÞHØÜš\�[˜Ý[Ûˆ‚Jˆ›Ú™XÝ[J -KˆYˆH]]܈Ù\È›Ý[ÙYžH\Ș[YKÙHÛ›ÝÈ‚JˆÈ˜[˜XÚÈÈYØXÞH™Z]š[܈[™\ÙHH›Ú™XÝ[H[�]IÜÈ‚Jˆ[Ù[�[\ZYÚ ˆ‚J‚‚JˆYˆH˜[YH\È[ž][™ÈÝ\ˆ[ˆSÑSÔÔQQÓ“Ó‘K[ˆÙHÛ›ÝÈ‚JˆH]]܈™\]Y\ÝYHÜXÚYšXÈ™[ØÚ]H -[˜ÛY[™È -H[™Ú[‚Jˆ\ÙH]]܈˜[YH[œÝXYÙˆ[Ù[�[\ZYÚ ‚‚J‹Â‚‚ZYˆ -›Ú™XÝ[KO�™[ØÚ]KžHOHSÑSÔÔQQÓ“Ó‘JB‚^‚B]ÜÜÊ[� [� O›[Ù[]Kš�[\ZYÚ -N‚_B‚Y[ÙB‚^‚B]ÜÜÊ[� ›Ú™XÝ[KO�™[ØÚ]KžJN‚_B‚B‚Kʈ\HÛÛ܈Y�\ÝY[� ˆ -‹Â‚X\WØÛÛÜ—ÜÙ]ØY�\Ý -[� \™[� ›Ú™XÝ[KO˜ÛÛÜ—ÜÙ]ØY�\Ý -N‚ˆ[� OœÜ]Û�\HHÔUÓ—ÕTWÔ“Ò‘PÕSWГÓPŽÂˆ[� O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘Nˆ[� O›ÝÛ™\ˆH\™[�Ȉ[� O››ÙܘXˆH NȈ[� O�Ù^ÙHH -VÑWÔ‘TT‘WÕÕPÒVÑWÔ‘TT‘WÑÔ“ÕS‘ -NȈˆ[� O�[šÈHÛÛ[[Û—Ý[šÎˆ[� O›™^[šÈHÝ[YH -È Nˆ[� O�ž[[Ý™HH•Sˆ[� O�ZÙXXÝ[ÛˆH•Sˆ[� O›[Ù[]K˜Z[[Ý™HHRSSÕ‘LWГÓPŽÂˆ[� O›[Ù[]K˜ZX]XÚÈHRPUPÒÌWÓ“ÐUPÒΈ[� O�ZÙY[XYÙHHÛÛ[[Û—ÝZÙY[XYÙN‚Y[� O˜]]ÚÚ[ �H�UUÒÒSÐUPÒ×ÒUÈ‚‚ZYˆ -[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ ˆSÕ‘WÐÓÓ‘’Q×Ó“×ÓSÕ‘JB‚^‚BY[� O˜]]ÚÚ[HUUÒÒSÐS’SPUSÓ—ÐÓÓTUN‚_B‚Bˆ[� OœÜYY][H ŽÂ‚‚KÊ‚ˆ -ˆÛÜH˜XÝ[Ûˆ]K‚ˆ -‹Â‚‚Y˜XÝ[Û—ØÛÜWØ[ -[� \™[� -Nˆ‚Y[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ �H“SÕ‘WÐÓÓ‘’Q×Ó“×ÐQ•TÕÐ�TÑN‚Y[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈH -SÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×Ð�TÑSPTSÕ‘WÐÓÓ‘’Q×Ô“Ò‘PÕSWÐ�TÑWÑQHSÕ‘WÐÓÓ‘’Q×Ô“Ò‘PÕSWÕÐSГÕS�ÑHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÑÔ�U’UHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÒÓHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÔU“Ô“HSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÕÐS -N‚B‚KËÈ^XÝ]HH›Ú™XÝ[IÜÈÛˆÜ]Ûˆ]™[� ‚‚Y^XÝ]WÛÛœÜ]Û—ÜØÜš\ -[� -N‚B‚\™]\›ˆ[�ŸB‚‹ËÈÜ]Ûˆ ÈÝ\œÂ‹Ë‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ NKLL‹LM‹Ë‹ËÈÜ]Ûˆ™YHœÝ\ˆˆ›Ú™XÝ[\ˈYX[�›ÜˆZZšH[™[ZY\È[ˆ‹ËÈÜšYÚ[˜[™X]ÈÙˆ˜YÙKÚÈÛÝ[�[\[™›ÝÈ™YHÝ\ˆ‹ËÈÚ\šZÙ[ˆXYÛÛ˜[HÝÛ�Ø\™]^Y\œËˆÜšYÚ[˜[]]܈‹ËÈ›Ù[ �][ÙYšYYÙ]™\˜[[Y\ÈžH[šÛ›ÝÛˆ\�Y\ˈ™Y˜XÝÜ™Y‹ËÈžHÈ Œ NKLL‹LMÈÈÛÜšÈÚ]Ý\œ™[�›Ú™XÝ[HÞ\Ý[Kˆ‹ËÈØ�š[Ý\ÛKÙH™YY\ț܈˜XÚÝØ\™ÛÛ\]Xš[]HÚ]YØXÞH‹ËÈ[Ù[\Ë�]Ý\�Ú\ÙHØÜš\\ÈH™\ÝÚÚXÙHÈ[™H‹ËÈ][\H›Ú™XÝ[\È܈[žHÝ\ˆÛÜ�ÙˆÜXÚX[^™Y‹ËțڙXÝ[HÜ]ۜ˂‹Ë‹ËÈ™]\›ˆ•QHYˆÝ\œÈÜ]Û™Y �SÑHÛˆ˜Z[ ‚š[�Ý\—ÜÜ]ÛŠ[�]H -œ\™[� ×ܛڙXÝ[H -œ›Ú™XÝ[JBžÂˆÛÛœÝ[�PVÔÕT”ÈH ΂ˆ[�]H -™[�H•S‚Z[�HH ‚Z[�[™^HSÑSÒS‘VÓ“Ó‘Nˆ[�š\œÝÜÛÜ�YH ‚‚\ר^\×Üš[˜Ú\[Ù›Ø]ÜÚ][ێ‚YWÙ\™XÝ[Ûˆ\™XÝ[ÛˆHT‘PÕSÓ—Ô’QÒ‚YWܛڙXÝ[WÜš[YH›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓ“Ó‘N‚‚KËÈYˆ\™IÜțțڙXÝ[H܈\™[�Ù][™Ë^]›ÝË‚‚ZYˆ -\›Ú™XÝ[H\\™[� -B‚^‚B\™]\›ˆ�SÑN‚_B‚BˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž‚\ÜÚ][Û‹žHH\™[� OœÜÚ][Û‹žH -țڙXÝ[KOœÜÚ][Û‹žN‚\ÜÚ][Û‹žˆH\™[� OœÜÚ][Û‹žˆ -țڙXÝ[KOœÜÚ][Û‹žŽÂ‚ˆ ËÈØ[YHÛÛ˜Ù\\ÈÛšY™HÜ]Û‹ˆÛÚț܈[Ù[ÈÜ]Û‹‚‚KËÈ Kˆ[š[X][Ûˆ›Ú™XÝ[K‚‚KËÈ ‹ˆÙX\Ûˆ[Ù[›Ú™XÝ[K‚‚KËÈ Ëˆ˜\ÙH[Ù[›Ú™XÝ[K‚‚KËÈ ˆYØXÞHY˜][ ‚ˆYŠ›Ú™XÝ[KOœÝ\ˆ�H -Bˆˆ[™^H›Ú™XÝ[KOœÝ\ŽÂ‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÔ“Ò—ÔÕTŽÂˆB‚Y[ÙHYˆ -\™[� O�ÙX\[� ‰ˆ\™[� O�ÙX\[� O›[Ù[]KœÝX�\HOHÕP•TWÔ“Ò‘PÕSH ‰ˆ\™[� O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ�H -B‚^‚BZ[™^H\™[� O�ÙX\[� O›[Ù[]Kœ›Ú™XÝ‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÕÑPTÓ—Ô“Ò‘PÕSN‚_Bˆ[ÙHYŠ\™[� O›[Ù[]KœÝ\ˆ�H -Bˆˆ[™^H\™[� O›[Ù[]KœÝ\ŽÂ‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÓSÑSÔÕTŽÂˆBˆ[ÙBˆˆ[™^HÙ]ØØXÚYÛ[Ù[Ú[™^ -”Ý\ˆŠNÈ‚‚B\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÔÓÕT�ÑWÑÓÐ�SÔÕTŽÂˆB‚‚\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÐ�TÑWÖN‚\›Ú™XÝ[WÜš[YHH“Ò‘PÕSWÔ’SQWÓUS�ÒÓSÕ’S‘΂‚KËÈÛÜÈX^Ý\ˆÛÝ[� ‚ˆ›ÜŠHH ÈHPVÔÕT”ÎÈJÊÊBˆ‚BKËÈÜ]ÛˆHÝ\ˆ[�]KˆYˆÙH˜Z[ ^][™™]\›ˆ˜[ÙK‚ˆ[�HÜ]ÛŠÜÚ][Û‹ž ÜÚ][Û‹ž‹ÜÚ][Û‹žK\™XÝ[Û‹•S [™^ •S -NˆYŠ[�OH•S -Bˆˆ™]\›ˆ ˆB‚ˆ ËÈÙ]™\Ý[Ùˆ\™XÝ[ÛˆY�\ÝY[� ˆÙH™YY\È™Y›Ü™HÙHØ[ˆ[™Bˆ ËÈÜÚ][Ûš[™ÈÛˆ^\Ë‚ˆ\™XÝ[ÛˆH\™XÝ[Û—ÙÙ]ØY�\ÝY[�Ü™\Ý[ -[� \™[� ›Ú™XÝ[KO™\™XÝ[Û—ØY�\Ý -N‚ˆ Ê‚ˆ -ˆ\HY˜][ÜÚ][ÛˆY‚ˆ -ˆÜ™X]܈Y›ÝÚ]™H\ÈH˜[YK‚ˆ -‹Â‚ˆYˆ -›Ú™XÝ[KOœÜÚ][Û‹žOH“Ò‘PÕSWÓQÐPÖWÐÓÓTUP’SUWÔÔÒUSÓ—Ö -Bˆˆ›Ú™XÝ[KOœÜÚ][Û‹žH“Ò‘PÕSWÑQ�USÔÕT—ÔÔÒUSÓ—ÖˆB‚ˆ ËÈ™]™\œÙHÚ[ˆ\™[�˜XÙ\ÈY� ‚ˆYˆ -\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž -țڙXÝ[KOœÜÚ][Û‹žˆBˆ[ÙBˆˆÜÚ][Û‹žH\™[� OœÜÚ][Û‹ž H›Ú™XÝ[KOœÜÚ][Û‹žˆB‚ˆ[� OœÜÚ][Û‹žHÜÚ][Û‹žˆ[� O™\™XÝ[ÛˆH\™XÝ[ێ‚‚BKËÈ Œ NKLL‹LMÈÈ H›ÝÝ\™HÚHÙHÙ]]XÚÚ[™ÈÙ™‹�]‚BKËÈX]š[™È]\™H›ÜˆYØXÞH™Z]š[Ü‹‚ˆ\™[� O˜]XÚÚ[™ÈHUPÒÒS‘×Ó“Ó‘N‚‚BKËÈš\œÝÝ\ˆÜ]Û™YÛÜ�YÙ\�™\È\ÈH˜\ÙH›ÜˆÛÜ�[™Ëˆ‚BKËÈ[ˆH™^Ý\‰ÜÈÛÜ�\ÈH˜\ÙH HÛÜ[™^ ˆXXÚ‚BKËÈÝXœÙ\]Y[�Ý\ˆ\X\œÈÛ™HÝ\�\�\ˆ™Z[™[‚‚BKËÈÛÜ�[™ÈÜ™\‹ˆ‚BKË‚BKËÈ^ˆ˜\ÙH -š\œÝÝ\ŠHH Œ  Œ H HH NK Œ H ˆH N ‚‚BZYˆ -HH -B‚B^‚BBYš\œÝÜÛÜ�YH[� OœÛÜ�Y‚B_B‚ˆ[� OœÛÜ�YHš\œÝÜÛÜ�Y HN‚ˆ[� O�ZÙY[XYÙHH\œ›Ý×ÝZÙY[XYÙNˆ[� O›ÝÛ™\ˆH\™[�ˆ[� O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘Nˆ[� O››ÙܘXˆH Nˆ‚BKËÈÙ]HÝ\ˆ™[ØÚ]HÙ][™Èœ›ÛH[š[X][Û‹‚‚BY[� O�™[ØÚ]KžH›Ú™XÝ[KOœÝ\—Ý™[ØÚ]VÚWN‚‚BKËÈ™]™\œÙH™[ØÚ]HYˆ\™XÝ[Ûˆ\ÈY� ‚‚BZYˆ -\™XÝ[ÛˆOHT‘PÕSÓ—ÓQ• -B‚B^‚BBY[� O�™[ØÚ]KžH Y[� O�™[ØÚ]Kž‚B_B‚BB‚BY[� O�[šÈHÛÛ[[Û—Ý[šÎˆ[� O›™^[šÈHÝ[YH -È Nˆ[� O�ž[[Ý™HH•Sˆ[� O�ZÙXXÝ[ÛˆH•Sˆ[� O›[Ù[]K˜Z[[Ý™HHRSSÕ‘LWÔÕTŽÂˆ[� O›[Ù[]K˜ZX]XÚÈHRPUPÒÌWÓ“ÐUPÒΈ‚BKËÈ™[[Ý™HÝ\ˆÛˆÛÛ�XÝ ‚‚BZYˆ -[� O›[Ù[]Kœ™[[Ý™WØÛÛ™šYÈ ˆ‘SSÕ‘WÐÓÓ‘’Q×ÒU -B‚B^‚BBY[� O˜]]ÚÚ[HUUÒÒSÐUPÒ×ÒU‚B_B‚BBBB‚BY[� OœÜÚ][Û‹žHHÜÚ][Û‹žN‚BY[� O˜˜\ÙHHÜÚ][Û‹žNˆ[� OœÜYY][H ŽÂˆˆ Ê‚ˆ -ˆÛÜH˜XÝ[Ûˆ]K‚ˆ -‹Âˆˆ˜XÝ[Û—ØÛÜWØ[ -[� \™[� -N‚‚BKËȘ\ÚXÈ\œšX[ˆ›Ü\�HÙ]\ ‚‚BY[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈH -SÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×Ð�TÑSPTSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÑÔ�U’UHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÒÓHSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÔU“Ô“HSÕ‘WÐÓÓ‘’Q×ÔÕP’‘PÕÕ×ÕÐS -N‚BY[� O›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈ �H“SÕ‘WÐÓÓ‘’Q×Ó“×ÐQ•TÕÐ�TÑN‚ˆ[� OœÜ]Û�\HHÔUÓ—ÕTWÔ“Ò‘PÕSWÔÕTŽÂ‚BY[� Oœ›Ú™XÝ[WÜš[YHH›Ú™XÝ[WÜš[YN‚‚BKËÈ^XÝ]HH›Ú™XÝ[IÜÈÛˆÜ]Ûˆ]™[� ‚‚BY^XÝ]WÛÛœÜ]Û—ÜØÜš\ -[� -NˆBˆ™]\›ˆ•QNŸB‚‚‚�›ÚYÝX[WÝ[šÊ -BžÂˆYŠ\Ù[‹O˜[š[X][™ÊBˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÔÕPSWÐS’SPUSÓ—ÐÓÓTUJNˆ™]\›ŽÂˆB‚ˆÙ[‹O˜˜\ÙH -ÏH NˆÙ[‹OœÜÚ][Û‹žHHÙ[‹O˜˜\ÙNŸB‚‚‚‹Ëț܈H�˜\ˆ\H ËLKLŒ H˜\Ý\��›ÚY˜\Ý[šÊ -BžÂˆÙ[‹O˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘NŸB‹ËÈ ËLKLŒ H˜\[™‚‚‚‚�›ÚYÝX[WÜÜ]ÛŠ›Ø] ›Ø]‹›Ø]JBžÂˆ[�]H -™HH•S‚ˆHHÜ]ÛŠ ‹K ”ÝX[H‹ LK•S -N‚ˆYŠHOH•S -Bˆˆ™]\›ŽÂˆB‚ˆKOœÜ]Û�\HHÔUÓ—ÕTWÔÕPSNˆKO˜˜\ÙHHNˆKO›[Ù[]K›[Ý™WØÛÛ™šY×Ù›YÜÈHSÕ‘WÐÓÓ‘’Q×Ó“×ÐQ•TÕÐ�TÑNˆKO�[šÈHÝX[WÝ[šÎ‚‚KËÈ^XÝ]HHÝX[\ÉÜÈÛˆÜ]Ûˆ]™[� ‚‚Y^XÝ]WÛÛœÜ]Û—ÜØÜš\ -JNŸB‚‚‚�›ÚYÝX[Y\—Ý[šÊ -BžÂˆÝX[WÜÜ]ÛŠÙ[‹OœÜÚ][Û‹ž Ù[‹OœÜÚ][Û‹ž‹Ù[‹OœÜÚ][Û‹žJNˆÙ[‹O›™^[šÈHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È L -H -È -˜[™ ÌŠ -H ˆ ÌJNŸB‚‚‚�›ÚY^Ý[šÊ -H ËÈ™]È�[˜Ý[ÛˆÛÈ^Ø[ˆ™H\Ü^YYžÂˆ ËÈØZ]ÈÝZXÚYBˆYŠ\Ù[‹O˜[š[X][™ÊBˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÕVÐS’SPUSÓ—ÐÓÓTUJNˆBŸB‚‹ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË‚‹ËÚÛZ[™È\œ›ÝÈš[™]È\™Ù]‹ËÈ\Hˆ\™Ù]\B™[�]H -šÛZ[™×Ùš[™Ý\™Ù] -[�]JˆXÝ[™×Ù[�]JBžÂˆ[�Nˆ[�Z[ŽÂˆ[�X^ˆˆ[�]Jˆ\™Ù]Ù[�]HH•Sˆ[�]JˆÝ\œÛÜ—Ù[�]N‚ˆ ËÝ\ÙHHØ[È[š[X][Û‰ÜȘ[™ÙBˆYŠ˜[Y[š[JXÝ[™×Ù[�]KS’WÕÐSÊJBˆˆZ[ˆHXÝ[™×Ù[�]KO›[Ù[]K˜[š[X][Û–ÐS’WÕÐS×KOœ˜[™ÙKž ›Z[ŽÂˆX^HXÝ[™×Ù[�]KO›[Ù[]K˜[š[X][Û–ÐS’WÕÐS×KOœ˜[™ÙKž ›X^ˆBˆ[ÙBˆˆZ[ˆH ˆX^HNNNˆB‚ˆ ËÙš[™H Û™X\™\Ý ÈÛ™Bˆ›ÜŠHH ÈH[�ÛX^ÈJÊÊBˆˆÝ\œÛÜ—Ù[�]HH[�Û\ÝÚWN‚ˆYŠÝ\œÛÜ—Ù[�]KO™^\ÝÈ ‰ˆÝ\œÛÜ—Ù[�]HOHXÝ[™×Ù[�]H ËÈØ[�\™Ù]Ù[‚ˆ ‰ˆÝ\œÛÜ—Ù[�]HOHXÝ[™×Ù[�]KO›ÝÛ™\ˆ ËÈÛ‰ÝÛÈY�\ˆÝÛ™\‹‚ˆ ‰ˆ˜XÝ[Û—ØÚXÚ×Ú\×ÚÜÝ[JXÝ[™×Ù[�]KÝ\œÛÜ—Ù[�]JBˆ ‰ˆY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž -H -ÈY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]KOœÜÚ][Û‹žŠH�HZ[‚ˆ ‰ˆY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž -H -ÈY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]KOœÜÚ][Û‹žŠHHX^ˆ ‰ˆÝ\œÛÜ—Ù[�]KO˜[š[X][Û‹O��[™\˜X›VØÝ\œÛÜ—Ù[�]KO˜[š[\Ü×H -BˆˆYŠ]\™Ù]Ù[�]HY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž -H -ÈY™ŠÝ\œÛÜ—Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]KOœÜÚ][Û‹žŠHY™Š\™Ù]Ù[�]KOœÜÚ][Û‹ž XÝ[™×Ù[�]KOœÜÚ][Û‹ž -H -ÈY™Š\™Ù]Ù[�]KOœÜÚ][Û‹ž‹XÝ[™×Ù[�]KOœÜÚ][Û‹žŠJBˆˆ\™Ù]Ù[�]HHÝ\œÛÜ—Ù[�]NˆBˆBˆBˆˆ™]\›ˆ\™Ù]Ù[�]NŸB‚‚�›ÚYšZÙWØÜ˜\Ú - -BžÂˆ[�NˆYŠÙ[‹O™\™XÝ[ÛˆOHT‘PÕSÓ—Ô’QÒ -BˆˆÙ[‹O�™[ØÚ]KžH ŽÂˆBˆ[ÙBˆˆÙ[‹O�™[ØÚ]KžH LŽÂˆBˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYˆ -Ø]™Y]Kš›Þ\�[X›VÚWJHÛÛ�›ÛÜ�[X›JK K L -NˆBˆ ËÚYŠÙ[‹OœÜÚ][Û‹žY˜[˜Ù^ LL Ù[‹OœÜÚ][Û‹žˆY˜[˜Ù^ -ÊšY[Û[Ù\Ëš™\ÊÌL -JHÚ[Ù[�]JÙ[ŠNŸB‚‚‚š[�šZÙ\—ÝZÙY[XYÙJ[�]Jˆ\™Ù]Ù[�]K[�]Jˆ]XÚÚ[™×Ù[�]Kר]XÚʈ]XÚ×ÛØš™XÝ [�˜[Ù›YËÛÛœÝ×ÙY™[œÙJˆY™[œÙWÛØš™XÝ -BžÂˆ[�]H -™š]™\ˆH•S‚ˆYŠ\™Ù]Ù[�]KO™X]ÜÝ]H ˆPUÔÕUWÑPQ -Bˆˆ™]\›ˆ ˆBˆ ËÈ™[[ˆHÛBˆYŠ\™Ù]Ù[�]KOœÜÚ][Û‹žHUÑT -BˆˆÚ[Ù[�]J\™Ù]Ù[�]KÒSÑS•UWÕ’QÑÑT—ÕRÑWÑSPQÑWÐ’RÑT—ÔU -Nˆ™]\›ˆ ˆBˆYŠ]XÚÚ[™×Ù[�]HOH\™Ù]Ù[�]JBˆˆÙ]ÛÜÛ™[� -]XÚÚ[™×Ù[�]K\™Ù]Ù[�]JNˆB‚ˆYŠ]XÚ×ÛØš™XÝ O››×ÜZ[ŠH ËÈۉݛܚ]™\ˆ[�[]\ÈXY ™XØ]\ÙHH]XÚÈ\È›ÈZ[ˆY™™X݈ˆÚXÚÙ[XYÙJ\™Ù]Ù[�]K]XÚÚ[™×Ù[�]K]XÚ×ÛØš™XÝ Y™[œÙWÛØš™XÝ -NˆYŠ\™Ù]Ù[�]KO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ -Bˆˆ™]\›ˆ NÈ ËÈ›ÝXYY]ˆBˆB‚ˆÚXÚר˜XÚÜZ[Š]XÚÚ[™×Ù[�]K\™Ù]Ù[�]JNˆÙ]ÜZ[Š\™Ù]Ù[�]K\™Ù]Ù[�]KO›\ÝÙ[XYÙWÝ\K JNˆ\™Ù]Ù[�]KO˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘NˆYŠ]\™Ù]Ù[�]KO›[Ù[]K›Ù™œØÜ™Y[šÚ[ -Bˆˆ\™Ù]Ù[�]KO›[Ù[]K›Ù™œØÜ™Y[šÚ[H L ˆBˆ\™Ù]Ù[�]KO�[šÈHšZÙWØÜ˜\Úˆ ËÈÙ[ \È\ÈH™X[[�]KHš]™\ˆÚÈZÙHH[XYÙBˆYŠ -š]™\ˆH›ÜÙš]™\Š\™Ù]Ù[�]JJJBˆˆš]™\‹OœÜÚ][Û‹žHH\™Ù]Ù[�]KOœÜÚ][Û‹žNˆš]™\‹O™›ÜH Nˆš]™\‹O™\™XÝ[ÛˆH\™Ù]Ù[�]KO™\™XÝ[ێˆYŠš]™\‹O�ZÙY[XYÙJBˆˆš]™\‹O�ZÙY[XYÙJš]™\‹]XÚÚ[™×Ù[�]K]XÚ×ÛØš™XÝ ˜[Ù›YËY™[œÙWÛØš™XÝ -NˆBˆ[ÙBˆˆš]™\‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[� OH]XÚ×ÛØš™XÝ O˜]XÚ×ٛܘÙNˆBˆBˆ\™Ù]Ù[�]KO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H ˆÚXÚÙX] -\™Ù]Ù[�]JNˆ™]\›ˆ NŸB‚‚‚�›ÚYØœÝXÛWÙ˜[ - -BžÂˆYŠ[˜Z\ŠÙ[ŠJBˆˆ™]\›ŽÂˆB‚ˆÙ[‹O�™[ØÚ]KžHÙ[‹O�™[ØÚ]KžˆH ˆYŠ -\Ù[‹O˜[š[X][™È ‰ˆ˜[Y[š[JÙ[‹S’WÑQJJH]˜[Y[š[JÙ[‹S’WÑQJJBˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÓДÕPÓWÑ�SÓ“×ÑPUÐS’SPUSÓŠNÈ ËÈš^YÛÈS’WÑQHØ[ˆ™H\ÙYˆBŸB‚‚‚�›ÚYØœÝXÛWÙ›J -H ËÈ›ÝÈØœÝXÛ\ÈØ[ˆ›HÚ[ˆ]ZÙHÛˆÚ[\ÛÛœËÕS•žÂˆ ËÜÙ[‹OœÜÚ][Û‹ž -ÏHÙ[‹O�™[ØÚ]Kž -ˆ È ËÈ\]Z]™[[�ÙˆÜYY ˆYŠÙ[‹OœÜÚ][Û‹žˆY˜[˜Ù^ -È -šY[Û[Ù\Ëš™\È -È Œ -HÙ[‹OœÜÚ][Û‹žY˜[˜Ù^ H Œ -BˆˆÚ[Ù[�]JÙ[‹ÒSÑS•UWÕ’QÑÑT—ÓДÕPÓWÑ“WÓÕUÓїГÕS‘ÊNˆBŸB‚‚‚š[�ØœÝXÛWÝZÙY[XYÙJ[�]Jˆ\™Ù]Ù[�]K[�]Jˆ]XÚÚ[™×Ù[�]Kר]XÚʈ]XÚ×ÛØš™XÝ [�˜[Ù›YËÛÛœÝ×ÙY™[œÙJˆY™[œÙWÛØš™XÝ -BžÂˆYŠ\™Ù]Ù[�]KOœÜÚ][Û‹žHHUÑT -BˆˆÚ[Ù[�]J\™Ù]Ù[�]KÒSÑS•UWÕ’QÑÑT—ÕRÑWÑSPQÑWÓДÕPÓWÔU -Nˆ™]\›ˆ ˆB‚ˆÙ]ÛÜÛ™[� -]XÚÚ[™×Ù[�]K\™Ù]Ù[�]JNˆYŠ\™Ù]Ù[�]KO›ÜÛ™[� ‰ˆ -\™Ù]Ù[�]KO›ÜÛ™[� O›[Ù[]K�\H ˆTWÔVQTŠJBˆˆYˆ -Ø]™Y]Kš›Þ\�[X›VÝ\™Ù]Ù[�]KO›ÜÛ™[� Oœ^Y\š[™^JHÛÛ�›ÛÜ�[X›J\™Ù]Ù[�]KO›ÜÛ™[� Oœ^Y\š[™^  K ÍJNˆBˆˆ ʈØ[Ý[]H[™\H[XYÙKˆ -‹ÂˆÚXÚÙ[XYÙJ\™Ù]Ù[�]K]XÚÚ[™×Ù[�]K]XÚ×ÛØš™XÝ Y™[œÙWÛØš™XÝ -N‚ˆ\™Ù]Ù[�]KOœ^Y\š[™^H]XÚÚ[™×Ù[�]KOœ^Y\š[™^È ËÈYYÛÈÚ[�ÈÛÈÈHÛÜœ™XÝ^Y\‚ˆYØÛÜ™J]XÚÚ[™×Ù[�]KOœ^Y\š[™^ ]XÚ×ÛØš™XÝ O˜]XÚ×ٛܘÙH -ˆ\™Ù]Ù[�]KO›[Ù[]K›][\JNÈ ËÈÚ[�ÈØ[ˆ›ÝÈ™HÚ]™[ˆ›Üˆ][™È[ˆØœÝXÛB‚ˆYŠ\™Ù]Ù[�]KO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H -Bˆ‚ˆÚXÚÙX] -\™Ù]Ù[�]JN‚ˆYŠ]XÚÚ[™×Ù[�]KOœÜÚ][Û‹ž\™Ù]Ù[�]KOœÜÚ][Û‹ž -Bˆˆ\™Ù]Ù[�]KO�™[ØÚ]KžH NˆBˆ[ÙBˆˆ\™Ù]Ù[�]KO�™[ØÚ]KžH LNˆB‚ˆ\™Ù]Ù[�]KO˜]XÚÚ[™ÈHUPÒÒS‘×ÐPÕU‘NÈ ËÈÛÈØœÝXÛ\ÈØ[ˆ^ÙH[™\�^Y\œËÙ[™[ZY\‚ˆYŠ\™Ù]Ù[�]KO›[Ù[]KœÝX�\HOHÕP•TWÑ“QQJH ËÈ›ÝÈØœÝXÛ\ÈØ[ˆ›HZÙHÛˆÚ[\ÛÛœËÕS•ˆˆ\™Ù]Ù[�]KO�™[ØÚ]Kž -�H ˆ\™Ù]Ù[�]KO�[šÈHØœÝXÛWÙ›Nˆ[�ÜÙ]Ø[š[J\™Ù]Ù[�]KS’WÑ�S  -NˆBˆ[ÙBˆˆ\™Ù]Ù[�]KO�[šÈHØœÝXÛWÙ˜[‚ˆYŠ˜[Y[š[J\™Ù]Ù[�]KS’WÑQJJBˆˆ[�ÜÙ]Ø[š[J\™Ù]Ù[�]KS’WÑQK -NÈ ËȈ KLLËL HYH™Y›Ü™HÜ܈Bˆ[ÙBˆˆÜÜÊ\™Ù]Ù[�]K\™Ù]Ù[�]KO›[Ù[]Kš�[\ZYÚ È KŒÌÌÊNˆ[�ÜÙ]Ø[š[J\™Ù]Ù[�]KS’WÑ�S  -NˆB‚ˆYŠ\™Ù]Ù[�]KO›[Ù[]K™X]ØÛÛ™šY×Ù›YÜÈ ˆPUÐÓÓ‘’Q×ÓPPÔ“×ГS’ÊBˆˆ\™Ù]Ù[�]KO˜›[šÈH NˆBˆBˆB‚ˆ\™Ù]Ù[�]KO›™^[šÈHÝ[YH -È Nˆ™]\›ˆ NŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ N L L�‹Ë‹ËÈ[ØØ]HY[[ÜžH[™Ù][�]H›Ü\�Y\È]Ú[™H˜[œÙ™\™Y‹ËÈÈH›ÜY][K‚�›ÚY[š]X[^™WÚ][WØØ\œžJ[�]H -™[� ×ÜÜ]Û—Ù[�žH -œÜ]Û—Ù[�žJBžÂˆ ËÈ] ÜÈÜÜÚX›HÈØ[\Èœ›ÛHØÜš\ ÛÈY‚ˆ ËÈ\™H\È[™XYHY[[ÜžH›Üˆ[ˆ][H[ØØ]Yˆ ËÈ\™KÛX\ˆ]Ý]ÈXZÙHÝ\™HÙHÛ‰Ý[™\ˆ ËÈÚ][žHY[[ÜžHXZÜË‚ˆYŠ[� Oš][WÜ›Ü\�Y\ÊBˆˆœ™YJ[� Oš][WÜ›Ü\�Y\ÊNˆ[� Oš][WÜ›Ü\�Y\ÈH•SˆB‚ˆ ËÈ[ØØ]HY[[ÜžH›ÜˆH][K‚ˆ[� Oš][WÜ›Ü\�Y\ÈHX[ØÊÚ^™[ÙŠ -™[� Oš][WÜ›Ü\�Y\ÊJNˆY[\Ù] -[� Oš][WÜ›Ü\�Y\Ë Ú^™[ÙŠ -™[� Oš][WÜ›Ü\�Y\ÊJN‚ˆYŠÜ]Û—Ù[�žJBˆˆ[� Oš][WÜ›Ü\�Y\ËOš[™^HÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\Ëš[™^‚ˆYŠÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\˘[X\ÖÌJBˆˆÝ˜ÜJ[� Oš][WÜ›Ü\�Y\ËO˜[X\ËÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\˘[X\ÊNˆB‚ˆYŠÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\˘ÛÛÜœÙ] -Bˆˆ[� Oš][WÜ›Ü\�Y\ËO˜ÛÛÜœÙ]HÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\˘ÛÛÜœÙ]ˆB‚ˆYŠÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\ËšX[ -Bˆˆ[� Oš][WÜ›Ü\�Y\ËOšX[HÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\ËšX[ˆBˆ[� Oš][WÜ›Ü\�Y\ËOœ^Y\—ØÛÝ[�HÜ]Û—Ù[�žKOš][WÜ›Ü\�Y\Ëœ^Y\—ØÛÝ[�ˆBŸB‚™[�]H -œÛX\�Ü]ÛŠ×ÜÜ]Û—Ù[�žH -œ›ÜÊH ËÈ ËLKLŒ H[�\™HÙXÝ[Ûˆ™\XÙYÚ]Ü™˜[ÈÛÙBžÂˆ[�]H -™HH•Sˆ[�]H -�ÜH•Sˆ[�^Y\˜ÛÝ[�‚ˆYŠ›ÜÈOH•S ʈˆ -]™[OH•S ‰‚ˆJØÜ™Y[—ÜÝ]\È ˆ -S—ÔÐÔ‘QS—ÔÑSPÕS—ÔÐÔ‘QS—ÕUHS—ÔÐÔ‘QS—ÒSÓÑ—Ñ�SQHS—ÔÐÔ‘QS—ÑÐSQWÓÕ‘TˆS—ÔÐÔ‘QS—ÔÒÕ×ÐÓÓTUHS—ÔÐÔ‘QS—ÑS‘ÒS‘WÐÔ‘QUS—ÔÐÔ‘QS—ÓQS•HS—ÔÐÔ‘QS—ÑÐSQWÔÕT•ÓQS•HS—ÔÐÔ‘QS—Ó‘U×ÑÐSQWÓQS•HS—ÔÐÔ‘QS—ÓÐQÑÐSQWÓQS•HS—ÔÐÔ‘QS—ÓÔSÓ”×ÓQS•HS—ÔÐÔ‘QS—ÐÓÓ•“ÓÓÔSÓ”×ÓQS•HS—ÔÐÔ‘QS—ÔÓÕS‘ÓÔSÓ”×ÓQS•HS—ÔÐÔ‘QS—Õ’QS×ÓÔSÓ”×ÓQS•HS—ÔÐÔ‘QS—ÔÖTÕSWÓÔSÓ”×ÓQS•JJH ‰‚ˆÝ\œ™[�ØÙ[™JJ‹Âˆ -Bˆˆ™]\›ˆ•SˆB‚ˆ ʈÜ]Ûˆ][H˜\ÙYÛˆ�[X™\ˆÙˆXÝ]™H^Y\œËˆ -‹ÂˆYŠ›ÜËOœÜ]Ûœ^Y\—ØÛÝ[��H -^Y\˜ÛÝ[�HPV - KÛÝ[�Ù[�ÊTWÔVQTŠJJJBˆˆYŠ›ÜËO˜›ÜÜÈ ‰ˆ]™[OH•S -Bˆˆ K[]™[ O˜›ÜÜÙ\ØÛÝ[�ˆBˆ™]\›ˆ•SˆB‚ˆYŠ]™[OH•S ‰ˆ - -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÒS•ÐT‘ -H -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓÕUÐT‘ -JH -BˆˆHHÜ]ÛŠ›ÜËOœÜÚ][Û‹ž ›ÜËOœÜÚ][Û‹žˆ -ÈY˜[˜Ù^K›ÜËOœÜÚ][Û‹žK›ÜËO™›\ ›ÜËO›˜[YK›ÜËOš[™^ ›ÜËO›[Ù[ -NˆBˆ[ÙBˆˆHHÜ]ÛŠ›ÜËOœÜÚ][Û‹ž -ÈY˜[˜Ù^ ›ÜËOœÜÚ][Û‹ž‹›ÜËOœÜÚ][Û‹žK›ÜËO™›\ ›ÜËO›˜[YK›ÜËOš[™^ ›ÜËO›[Ù[ -NˆB‚ˆYŠHOH•S -Bˆˆ™]\›ˆ•SˆB‚ˆ ËÜš[�Љ\Ë - Y‹ Y‹ YŠH H - Y‹ Y‹ YŠH‹›ÜËO›˜[YK›ÜËOœÜÚ][Û‹ž ›ÜËOœÜÚ][Û‹ž‹›ÜËOœÜÚ][Û‹žKKOœÜÚ][Û‹ž KOœÜÚ][Û‹ž‹KOœÜÚ][Û‹žJN‚ˆ ËÈ[X\ψYŠ›ÜËO˜[X\ÖÌJBˆˆY[\Ù] -KO›˜[YK Ú^™[ÙŠKO›˜[YJJNˆÝ˜ÜJKO›˜[YK›ÜËO˜[X\ÊNˆB‚ˆ ËÈYˆÙH]™H][H›Ü\�Y\È[ˆÜ]Ûˆ[�žK[ˆ™\\™HHÙ]Ù‚ˆ ËÈ›Ü\�Y\ÈÈ\ÜÈÛˆÈH][HÚ[ˆ]\È›ÜY ‚ˆYŠ›ÜËOš][JBˆˆ[š]X[^™WÚ][WØØ\œžJK›ÜÊNˆBˆˆ˜XÝ[Û—ØÛÜWÙ]J ™KO›[Ù[]K™˜XÝ[Û‹ œ›ÜËO™˜XÝ[ÛŠNˆ˜XÝ[Û—ØÛÜWÙ]J ™KO™˜XÝ[Û‹ œ›ÜËO™˜XÝ[ÛŠN‚ˆYŠ›ÜËOœÜ]Û�\JBˆˆKOœÜ]Û�\HH›ÜËOœÜ]Û�\NÈ ËÌŒ LWÌ ×ÌŒË\ÜÈÜ]Û�\K‚ˆB‚ˆYŠ›ÜËOšX[Ü^Y\˜ÛÝ[� H WHOH -BˆˆKO™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�HKO›[Ù[]KšX[H›ÜËOšX[Ü^Y\˜ÛÝ[� H WNˆB‚ˆYŠ›ÜËO›\OH -BˆˆKO™[™\™ÞWÜÝ]K›\ØÝ\œ™[�HKO›[Ù[]K›\H›ÜËO›\ˆB‚ˆYŠ›ÜËOœØÛÜ™HOH -BˆˆKO›[Ù[]KœØÛÜ™HH›ÜËOœØÛÜ™NÈ ËÈÝ™\�Üš]HØÛÜ™HYˆ^\ÝÈ[ˆH]™[ Üˈš[BˆBˆYŠ›ÜËO›][\HOH -BˆˆKO›[Ù[]K›][\HH›ÜËO›][\NÈ ËÈÝ™\�Üš]H][\HYˆ^\ÝÈ[ˆH]™[ ÜÈ �š[BˆB‚ˆYŠYKO›X\ ‰ˆ›ÜËO˜ÛÛÝ\›X\ -Bˆˆ[�ÜÙ]ØÛÛÝ\›X\ -K›ÜËO˜ÛÛÝ\›X\ -NˆB‚ˆYŠ›ÜËO˜YÙÜ™\ÜÚ[ÛŠBˆˆKO›[Ù[]K˜YÙÜ™\ÜÚ[ÛˆH›ÜËO˜YÙÜ™\ÜÚ[ÛŽÈ ËÈYÙÜ™\ÜÚ[ÛˆØ[ˆ™HÚ[™ÙYÚ]Ü]ÛˆÚ[�ț݈BˆYŠ›ÜËOš][WÜ›Ü\�Y\˘[JBˆˆKOš][WÜ›Ü\�Y\ËO˜[HH›ÜËOš][WÜ›Ü\�Y\˘[NˆBˆYŠ›ÜËO˜[JBˆˆKO›[Ù[]K˜[HH›ÜËO˜[NˆB‚ˆ ËÈ™Xˆ �‹ Œ H HÝÜ™HHÜšYÚ[˜[X\È™HX›HÈ™\ÝÜ™HÚ]Z[™È›\ÚˆYŠ›ÜËO™Z[™ÊBˆˆKO™Z[™ÈH›ÜËO™Z[™ÎÈ ËÈ™Xˆ �‹ Œ H H\ÙYÈYš[™HÚXÚÛÛÝ\›X\\È\ÙY›ÜˆHZ[™È›\ÚˆKOœ\ŒHH›ÜËOœ\ŒNÈ ËÈX\ˆ ŒK Œ H H\ÙYÈÝÜ™HÝ\ÝÛH\˜Ù[�YÙ\ˆKOœ\ŒˆH›ÜËOœ\ŒŽÈ ËÈX\ˆ ŒK Œ H H\ÙYÈÝÜ™HÝ\ÝÛH\˜Ù[�YÙ\ˆKO™Z[™ÌˆH›ÜËO™Z[™ÌŽÈ ËÈXÈ MK Œ Mˆ H\ÙYÈYš[™HÚXÚÛÛÝ\›X\\È\ÙY›ÜˆHZ[™È›\ڛ܈\ŒˆžHÚ]H˜YÛÛ‚ˆB‚ˆYŠ›ÜËO››ÛY™JBˆˆKO›[Ù[]K››ÛY™HH›ÜËO››ÛY™NÈ ËÈÝ™\�Üš]HÚ]\ˆ]™H\Èš\ÚX›H܈›ÝˆBˆKO˜›ÜÜÈH›ÜËO˜›ÜÜ΂ˆYŠ›ÜËO˜›ÜÜÈ ‰ˆ]™[OH•S ‰ˆ]™[ O˜›ÜÜÛ]\ÚXÖÌJBˆˆ]\ÚXÊ]™[ O˜›ÜÜÛ]\ÚXË K]™[ O˜›ÜÜÛ]\ÚX×ÛÙ™œÙ] -NˆB‚ˆ ËÈÚ]™HH[�]HHÙX\Ûˆ][BˆYŠ›ÜËO�ÙX\ÛŠBˆˆÜHÜ]ÛŠKOœÜÚ][Û‹ž USWÒQWÔÔÒUSÓ—Ö‹  ›ÜËO�ÙX\Û‹›ÜËO�ÙX\Ûš[™^ ›ÜËO�ÙX\Û›[Ù[ -NˆYŠÜ -Bˆˆ ËÙ[�ÙY˜][Ú[š] -Ü -NˆÙ]ÝÙX\ÛŠKÜ O›[Ù[]K�ÙX\Û—Ü›Ü\�Y\Ë�ÙX\Û—Ú[™^  -NˆKO�ÙX\[�HÜ‚ˆKO�ÙX\[� OœÜ]Û�\HHÔUÓ—ÕTWÕÑPTӎˆBˆB‚ˆ ËÈÙ][�]H\Nˆ^Y\‹[™[^KœË‹‹‚ˆYŠ›ÜËO™[�]]\JBˆˆKO›[Ù[]K�\HH›ÜËO™[�]]\NˆB‚ˆ ËÈÙ]H\™[�ˆYŠ›ÜËOœ\™[� -H ËËO�˜\›\Ý O�˜\œËO��OH•ÑSTBˆˆKOœ\™[�H -[�]H -Š\›ÜËOœ\™[�ˆB‚ˆ ËÙ[�ÙY˜][Ú[š] -JNˆ^XÝ]WÛÛœÜ]Û—ÜØÜš\ -JNˆ^XÝ]WÜÜ]Û—ÜØÜš\ -›ÜËJNˆ™]\›ˆNŸH ËÈ ËLKLŒ H™\XÙYÙXÝ[Ûˆ[™È\™B‚š[�\×Ú[˜Ø[J›Ø] ›Ø]‹›Ø]K›Ø]™\ÚÛ -BžÂˆYˆ -]™[ -BˆˆYˆ -�HY˜[˜Ù^ -Ý™\ÚÛ ‰ˆHY˜[˜Ù^ -ÝšY[Û[Ù\Ëš™\Ë]™\ÚÛ ‰ˆ‹XH�HY˜[˜Ù^JÝ™\ÚÛ ‰ˆ‹XHHY˜[˜Ù^JÝšY[Û[Ù\Ë�”™\ËM -HˆYˆ -�HØÜ›ÛZ[ž ‰ˆHØÜ›ÛX^ -ÝšY[Û[Ù\Ëš™\È ‰ˆˆ�HVQT—ÓRS—Öˆ ‰ˆˆHVQT—ÓPVÖˆ -Hˆ™]\›ˆ NˆBˆBˆB‚ˆ™]\›ˆ ŸB‚�›ÚYÜ]Ûœ^Y\Š[�[™^ -BžÂˆ×ÜÜ]Û—Ù[�žHˆ ËÜ×Û[Ù[ -ˆ[Ù[H•Sˆ[�Ø[ˆ[�˘˚[™H ˆ[™^ �H ΂‹ËÈ[Ù[Hš[™Û[Ù[ -^Y\–Ú[™^K›˜[YJN‹ËÈYŠ[Ù[OH•S -H™]\›ŽÂ‚ˆY[\Ù] - œ  Ú^™[ÙŠ -JNˆ ›˜[YHH^Y\–Ú[™^K›˜[YNˆ š[™^H LNˆ š][WÜ›Ü\�Y\Ëš[™^H LNˆ �ÙX\Ûš[™^H LNˆ ˜ÛÛÝ\›X\H^Y\–Ú[™^K˜ÛÛÝ\›X\ˆ œÜ]Ûœ^Y\—ØÛÝ[�H LNˆ œÜ]Û�\HHÔUÓ—ÕTWÔVQT—ÓPRSŽÂ‚ˆYŠ]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -BˆˆYŠ]™[ OœÜ]Ûˆ ‰ˆ]™[ OœÜ]Û–Ú[™^Kž -Bˆˆ œÜÚ][Û‹žH -›Ø] -JšY[Û[Ù\Ëš™\È H]™[ OœÜ]Û–Ú[™^Kž -NˆBˆ[ÙBˆˆ œÜÚ][Û‹žH -›Ø] -J -šY[Û[Ù\Ëš™\È H Œ -H H Ì -ˆ[™^ -NˆBˆBˆ[ÙBˆˆYŠ]™[ OœÜ]Ûˆ ‰ˆ]™[ OœÜ]Û–Ú[™^Kž -Bˆˆ œÜÚ][Û‹žH -›Ø] -J]™[ OœÜ]Û–Ú[™^Kž -NˆBˆ[ÙBˆˆ œÜÚ][Û‹žH -›Ø] -J Œ -È Ì -ˆ[™^ -NˆBˆ ™›\H NˆB‚ˆYŠ]™[ OœÜ]Ûˆ ‰ˆ]™[ OœÜ]Û–Ú[™^KžŠBˆˆYŠ]™[ OœØÜ›Û\ˆ ˆ -ÐÔ“ÓÒS•ÐT‘ÐÔ“ÓÓÕUÐT‘ -JBˆˆ œÜÚ][Û‹žˆH -›Ø] -J]™[ OœÜ]Û–Ú[™^KžŠNˆBˆ[ÙBˆˆ œÜÚ][Û‹žˆH -›Ø] -JVQT—ÓRS—Öˆ -È]™[ OœÜ]Û–Ú[™^KžŠNˆBˆBˆ[ÙHYŠVQT—ÓPVÖˆ HVQT—ÓRS—Öˆˆ JBˆˆ œÜÚ][Û‹žˆH -›Ø] -JVQT—ÓRS—Öˆ -È JNˆBˆ[ÙBˆˆ œÜÚ][Û‹žˆH -›Ø] -TVQT—ÓRS—֎ˆB‚ˆYŠ œÜÚ][Û‹žˆVQT—ÓRS—ÖŠBˆˆ œÜÚ][Û‹žˆHVQT—ÓRS—֎ˆBˆ[ÙHYŠ œÜÚ][Û‹žˆˆVQT—ÓPVÖŠBˆˆ œÜÚ][Û‹žˆHVQT—ÓPV֎ˆB‚ˆ ËËËËËËËËËËËËËËËËËØÚXÚÚ[™ÈÛ\ËÈØ[ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËˈ›ÜŠÈH ÈÈšY[Û[Ù\Ëš™\È È ÈÊÊÊBˆˆYŠ œÜÚ][Û‹ž�HšY[Û[Ù\Ëš™\ÊBˆˆ œÜÚ][Û‹ž OHšY[Û[Ù\Ëš™\ΈBˆYŠ œÜÚ][Û‹ž -Bˆˆ œÜÚ][Û‹ž -ÏHšY[Û[Ù\Ëš™\ΈBˆYŠVQT—ÓRS—ÖˆOHVQT—ÓPVÖŠBˆˆØ[HÚXÚÝØ[Ú[™^ -Y˜[˜Ù^ -È œÜÚ][Û‹ž  œÜÚ][Û‹žŠNˆYŠØ[�H  ‰ˆ]™[ O�Ø[ÖÝØ[KšZYÚPVÕÐSÒRQÒ -Bˆˆœ™XZÎÈ ËÙ›Ý[™ˆBˆYŠÚXÚÚÛWÚ[ŠY˜[˜Ù^ -È œÜÚ][Û‹ž  œÜÚ][Û‹ž‹ œÜÚ][Û‹žJH -Ø[�H  ‰ˆ]™[ O�Ø[ÖÝØ[KšZYÚ�HPVÕÐSÒRQÒ -JBˆˆš[™H ˆBˆ[ÙBˆˆœ™XZÎÈ ËÈ›Ý[™ˆBˆBˆ[ÙH›ÜŠ˜ÈH È˜È -VQT—ÓPVÖˆ HVQT—ÓRS—ÖŠH È ÎȘÊÊË œÜÚ][Û‹žˆ -ÏH ÊBˆˆYŠ œÜÚ][Û‹žˆˆVQT—ÓPVÖŠBˆˆ œÜÚ][Û‹žˆ OHVQT—ÓPVÖˆ HVQT—ÓRS—֎ˆBˆYŠ œÜÚ][Û‹žˆVQT—ÓRS—ÖŠBˆˆ œÜÚ][Û‹žˆ -ÏHVQT—ÓPVÖˆ HVQT—ÓRS—֎ˆBˆØ[HÚXÚÝØ[Ú[™^ -Y˜[˜Ù^ -È œÜÚ][Û‹ž  œÜÚ][Û‹žŠNˆYŠØ[�H  ‰ˆ]™[ O�Ø[ÖÝØ[KšZYÚPVÕÐSÒRQÒ -Bˆˆš[™H Nˆœ™XZΈBˆ[ÙHYŠØ[�H  ‰ˆ]™[ O�Ø[ÖÝØ[KšZYÚ�HPVÕÐSÒRQÒ -BˆˆÛÛ�[�YNˆBˆYŠÚXÚÚÛWÚ[ŠY˜[˜Ù^ -È œÜÚ][Û‹ž  œÜÚ][Û‹ž‹ œÜÚ][Û‹žJJBˆˆÛÛ�[�YNˆBˆš[™H Nˆœ™XZΈBˆYŠš[™ -Bˆˆœ™XZΈBˆ œÜÚ][Û‹ž -ÏH -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -HÈ Mˆ ˆBˆ ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËˈÝ\œ™[�Ü]Ûœ^Y\ˆH[™^ˆ^Y\–Ú[™^K™[�HÛX\�Ü]ÛŠ œ -N‚ˆYŠ^Y\–Ú[™^K™[�OH•S -Bˆˆ›Ü”Ú]ÝÛŠ K‘˜][ˆ[˜X›HÈÜ]Ûˆ^Y\ˆœ›ÛH É\É׈‹ ›˜[YJNˆB‚ˆ^Y\–Ú[™^K™[� Oœ^Y\š[™^H[™^‚ˆYŠ›ÛX^�\Ú™\Ù]ÍH�H JBˆˆ^Y\–Ú[™^K™[� Oœ�\Ú ›X^H›ÛX^�\Ú™\Ù]Ú[™^NˆBˆ[ÙBˆˆ^Y\–Ú[™^K™[� Oœ�\Ú ›X^H ˆB‚ˆY[\Ù] -ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]Ú\ÝÜžKˆ ˆÚ^™[ÙŠ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]Ú\ÝÜžJBˆ -N‚ˆY[\Ù] -ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YKˆ ˆÚ^™[ÙŠ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý[YJBˆ -N‚ˆY[\Ù] -ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—Ý[YKˆ ˆÚ^™[ÙŠ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—Ý[YJBˆ -N‚ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÜÝ\�ݘ[YH ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ݘ[YH ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]ØÛÝ[�H ˆ^Y\–Ú[™^K˜ÛÛ[X[™Ú[œ]Ú[™^H ‚ˆYŠ^Y\–Ú[™^KœÜ]ÛšX[ -Bˆˆ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H^Y\–Ú[™^KœÜ]ÛšX[ -È NˆBˆYŠ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ^Y\–Ú[™^K™[� O›[Ù[]KšX[ -Bˆˆ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H^Y\–Ú[™^K™[� O›[Ù[]KšX[ˆB‚ˆ ËÛ\]H™XÛÜ�™\ˆY�\ˆH]™[žHZ[ˆYŠ^Y\–Ú[™^KœÜ]Û›\ -Bˆˆ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�H^Y\–Ú[™^KœÜ]Û›\ -È ŽÂˆBˆYŠ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�ˆ^Y\–Ú[™^K™[� O›[Ù[]K›\ -Bˆˆ^Y\–Ú[™^K™[� O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�H^Y\–Ú[™^K™[� O›[Ù[]K›\ˆB‚ˆYŠ^Y\–Ú[™^K�ÙX\�[JBˆˆÙ]ÝÙX\ÛŠ^Y\–Ú[™^K™[� ^Y\–Ú[™^K�ÙX\�[K -NˆBˆ[ÙBˆˆÙ]ÝÙX\ÛŠ^Y\–Ú[™^K™[� ]™[ OœÙ]ÙX\  -NˆBŸB‚š[�›×Ü^Y\—Ø[]™WÝ×Ú›Ú[Š -BžÂˆ[�›×Ø[]™WÜ^Y\œÈH ˆ[�Nˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYŠ - -\^Y\–ÚWK™[�^Y\–ÚWK›]™\ÈH  -^Y\–ÚWK›]™\ÈH H ‰ˆ^Y\–ÚWK™[� O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�H -JH ‰‚ˆ - -›ÜÚ\™H ‰ˆ^Y\–ÚWK˜Ü™Y]ÈH -H -[›ÜÚ\™H ‰ˆÜ™Y]ÈH -JJBˆ -Bˆˆ -ÊÛ›×Ø[]™WÜ^Y\œÎˆBˆBˆ›×Ø[]™WÜ^Y\œÈH -›×Ø[]™WÜ^Y\œÈ�HPVÔVQT”ÊHÈ Hˆ ‚ˆ™]\›ˆ›×Ø[]™WÜ^Y\œÎŸB‚�›ÚYÚ[Ø[Ü^Y\œ×ØžWÝ[Y[Ý™\Š -BžÂˆ[�Nˆר]XÚÈ]XÚ×Ý[Y[Ý™\ˆH[\X]XÚΈר]XÚÈ]XÚ×ÛÜÙHH[\X]XÚΈÛÛœÝ×ÙY™[œÙJˆY™[œÙWÛØš™XÝH•S‚ˆ]XÚ×Ý[Y[Ý™\‹˜]XÚ×Ý\HHU×ÕSQSÕ‘TŽÂˆ]XÚ×Ý[Y[Ý™\‹™›Ü‹žHHY˜][Û[Ù[ٛ܋žNˆ]XÚ×Ý[Y[Ý™\‹™›Ü‹žHY˜][Û[Ù[ٛ܋žˆ]XÚ×Ý[Y[Ý™\‹™›Ü‹žˆHY˜][Û[Ù[ٛ܋žŽÂ‚ˆ]XÚ×ÛÜÙK˜]XÚ×Ý\HHU×ÓÔÑNˆ‚ˆ[™Ø[YHH Nˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆ[�]Jˆ\HÙ[ŽÂˆÙ[ˆH^Y\–ÚWK™[�ˆYŠÙ[ˆ ‰ˆ]˜[Y[š[JÙ[‹S’WÓÔÑJJBˆˆ[™Ø[YHH ˆ]XÚ×Ý[Y[Ý™\‹˜]XÚ×ٛܘÙHHÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�‚ˆY™[œÙWÛØš™XÝHY™[œÙWÙš[™ØÝ\œ™[�ÛØš™XÝ -Ù[‹•S ]XÚ×Ý[Y[Ý™\‹˜]XÚ×Ý\JNˆˆÙ[‹O�ZÙY[XYÙJÙ[‹Ù[‹ ˜]XÚ×Ý[Y[Ý™\‹ Y™[œÙWÛØš™XÝ -NˆBˆ[ÙHYŠÙ[ŠBˆˆ[™Ø[YHH ‚ˆ]XÚ×ÛÜÙK˜]XÚ×ٛܘÙHHÙ[‹O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆYˆ -[˜Z\ŠÙ[ŠH ‰ˆ˜[Y[š[JÙ[‹S’WÑ�SÔÑJJBˆˆ]XÚ×ÛÜÙK™›Ü‹žHHY˜][Û[Ù[ٛ܋žNˆ]XÚ×ÛÜÙK™›Ü‹žHY˜][Û[Ù[ٛ܋žˆ]XÚ×ÛÜÙK™›Ü‹žˆHY˜][Û[Ù[ٛ܋žŽÂˆÙ[‹O›[Ù[]K™X]ØÛÛ™šY×Ù›YÜÈHPUÐÓÓ‘’Q×ÓPPÔ“×ÑPUÑ�SÓS‘ˆBˆ[ÙBˆˆÙ[‹O›[Ù[]K™X]ØÛÛ™šY×Ù›YÜÈHPUÐÓÓ‘’Q×ÓPPÔ“×ÑPUˆB‚ˆY™[œÙWÛØš™XÝHY™[œÙWÙš[™ØÝ\œ™[�ÛØš™XÝ -Ù[‹•S ]XÚ×ÛÜÙK˜]XÚ×Ý\JNˆˆÙ[‹O�ZÙY[XYÙJÙ[‹Ù[‹ ˜]XÚ×ÛÜÙK Y™[œÙWÛØš™XÝ -NˆBˆÙ[ˆH\ˆBŸB‚�›ÚY[YWÛÝ™\Š -BžÂˆYŠ]™[ O�\HOH JBˆˆ]™[ØÛÛ\]YH NÈ ËÈ™Xˆ �K Œ H H\ÙY›Üˆ›Û�\È]™[ÈÛÈHY™H\Û‰ÝZÙ[ˆ]Ø^HYˆ[YH^\™\Ë›]™[ O�\HOH HYX[œÈ›Û�\È]™[ [ÙH™YÝ[\‚ˆBˆ[ÙHYŠ[]™[ØÛÛ\]Y -BˆˆÚ[Ø[Ü^Y\œ×ØžWÝ[Y[Ý™\Š -N‚ˆYˆ -Z\×ÝÝ[Ý[Y[Ý™\ŠBˆˆYŠÛؘ[ÜØ[\WÛ\Ý �[YWÛÝ™\ˆ�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý �[YWÛÝ™\‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ[Y[Y�H]™[ OœÙ][YH -ˆÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYYÈ ËÈ™Xˆ �  Œ H H\È[™H[Ý™Y\™HÈÙ]Ý\ÝÛH[YBˆYŠY[™Ø[YJBˆˆÚÝÝ[Y[Ý™\ˆH NˆBˆBˆYˆ -Z\×ÝÝ[Ý[Y[Ý™\ˆ ‰ˆ›×Ü^Y\—Ø[]™WÝ×Ú›Ú[Š -JBˆˆ\×ÝÝ[Ý[Y[Ý™\ˆH Nˆ›Ú›Ú[ˆH NˆBˆBŸB‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKH\]H�[˜Ý[ÛœÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚�›ÚY\]WÜØÜ›Û\Š -BžÂˆ›Ø]ÈH ˆ[�KYØZ[œÝ[™H ˆ[��[\^HH È ËÍ^Y\‚ˆ›Ø]HY˜[˜Ù^ HHY˜[˜Ù^Nˆ›Ø]›HH NNNNKHHNNNNNK›HH NNNNKHHNNNNNNÈ ËÜ^Y\ˆ›Ý[™\žH›ÞˆÝ]XÈ[�ØÜ›ÛYH ˆØÜ›ÛHØÜ›ÛHH ˆ[�Ø[]™HH ‚ˆYŠÝ[YH]™[ O˜Y˜[˜Ù][YHœ™Y^™X[ -Bˆˆ™]\›ŽÈ ËÈYYœ™Y^™X[ÛȘXÚÙÜ›Ý[™ËÜØÜ›Û[™ÈÛ‰Ý\]HYˆ[š[X][ÛœÈ\™Hœ›Þ™[‚ˆB‚ˆ Ê‚ˆKËÛ]™[ O˜Y˜[˜Ù][YHHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY ÌL -NÈ ËÈÚ[™ÙYÛÈØÜ›Û[™ÈÜYYÈ\›Üˆ˜\Ý\ˆ^Y\œÂˆ[]™[ O˜Y˜[˜Ù][YHHÝ[YH BˆBJ -^Y\–ÌK™[� ‰ˆ -^Y\–ÌK™[� O›[Ù[]KœÜYY ž�H Lˆ^Y\–ÌK™[� O›[Ù[]Kœ�[œÜYY�H LŠJHˆBH -^Y\–ÌWK™[� ‰ˆ -^Y\–ÌWK™[� O›[Ù[]KœÜYY ž�H Lˆ^Y\–ÌWK™[� O›[Ù[]Kœ�[œÜYY�H LŠJHˆBH -^Y\–Ì—K™[� ‰ˆ -^Y\–Ì—K™[� O›[Ù[]KœÜYY ž�H Lˆ^Y\–Ì—K™[� O›[Ù[]Kœ�[œÜYY�H LŠJHˆBH -^Y\–Ì×K™[� ‰ˆ -^Y\–Ì×K™[� O›[Ù[]KœÜYY ž�H Lˆ^Y\–Ì×K™[� O›[Ù[]Kœ�[œÜYY�H LŠJH -NÈ ËÈÚ[™ÙYÛÈYˆ[Ý\ˆ^Y\ˆ\Ș\Ý\ˆH˜XÚÙÜ›Ý[™ÈØÜ›Û˜\Ý\Š‹Â‚ˆ]™[ O˜Y˜[˜Ù][YHHÝ[YN‚ˆYŠ]™[ØÛÛ\]Y -Bˆˆ™]\›ŽÂˆB‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -^Y\–ÚWK™[� ‰ˆJ^Y\–ÚWK™[� O™X]ÜÝ]H ˆPUÔÕUWÑPQ -JBˆˆØ[]™HH Nˆœ™XZΈBˆB‚ˆ ËÕÚ]H˜YÛÛŽˆ›È[Ü™H[™[ZY\ÈBˆYŠÝ\œ™[�ÜÜ]Ûˆ�H]™[ O›�[\Ü]ÛœÈ ‰ˆYš[™[� -TWÑS‘SVJH ‰ˆØ[]™JBˆˆYŠYš[™[� -TWÑS‘U‘S -H ‰ˆ - -Yš[™[� -TWÒUSHTWÓДÕPÓJH ‰ˆ]™[ O�\HOH JH]™[ O�\HOH -JH ËÈ™Xˆ �K Œ H HYYÛÈØœÝXÛ\ˆˆ]™[ØÛÛ\]YH NÈ ËÈØ[ˆ™H\ÙY›Üˆ›Û�\È]™[ˆBˆBˆ[ÙHYŠÛÝ[�Ù[�ÊTWÑS‘SVJHÜ›Ý\Z[ŠBˆˆÚ[JÛÝ[�Ù[�ÊTWÑS‘SVJHÜ›Ý\X^ ‰‚ˆÝ\œ™[�ÜÜ]Ûˆ]™[ O›�[\Ü]ÛœÈ ‰‚ˆ]™[ OœÜÈ�H]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K˜]ˆ -BˆˆYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›]\ÚXÙ˜YJBˆˆ]\ÚXÙ˜YVÌHH -›Ø] -[]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›]\ÚXÙ˜YNˆ]\ÚXÙ˜YVÌWHH -›Ø] -\Ø]™Y]K›]\ÚXݛۈBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›]\ÚXÖÌJBˆˆÝ›˜ÜJ]\ÚXÛ˜[YK]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›]\ÚXËPVЕQ‘‘T—ÓSŠNˆ]\ÚXÛÙ™œÙ]H]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›]\ÚXÛÙ™œÙ]ˆ]\ÚXÛÛÜH NˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K�ØZ] -Bˆˆ]™[ O�ØZ][™ÈH NˆÛ×Ý[YHH ˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K™Ü›Ý\Z[ˆ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K™Ü›Ý\X^ -BˆˆÜ›Ý\Z[ˆH]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K™Ü›Ý\Z[ŽÂˆÜ›Ý\X^H]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K™Ü›Ý\X^ˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K››Ú›Ú[ˆOH -Bˆˆ›Ú›Ú[ˆH -]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K››Ú›Ú[ˆOH JNˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛZ[žˆ ˆ  -BˆˆØÜ›ÛZ[žˆH -›Ø] -J]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛZ[žˆ ˆ  Ù™™™™™™ŠNˆØÜ›ÛX^ˆH -›Ø] -[]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛX^ŽÂˆYŠWÝ[YJBˆˆY˜[˜Ù^HHØÜ›ÛZ[žŽÈ ËÈ™\Ù]HYˆÜ]Ûˆ]™\žH™YÚ[›š[™ÂˆBˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛZ[ž ˆ  -BˆˆØÜ›ÛZ[žH -›Ø] -J]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛZ[ž ˆ  Ù™™™™™™ŠNˆØÜ›ÛX^H -›Ø] -[]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœØÜ›ÛX^ˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K˜›ØÚØYJBˆˆ ËÈ\ÜÝ[YH]™[Ü]Ûˆ[�žHÚ[›Ý›Û˜XÚËÛÈ�\ÝÚ[™ÙH]È \™BˆYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K˜›ØÚØYH -Bˆˆ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K˜›ØÚØYHH ˆBˆ›ØÚØYHH -›Ø] -[]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K˜›ØÚØYNˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—Kœ[]HOH -Bˆˆ ËÈ\ÜÝ[YH]™[Ü]Ûˆ[�žHÚ[›Ý›Û˜XÚËÛÈ�\ÝÚ[™ÙH]È \™BˆYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—Kœ[]H -Bˆˆ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—Kœ[]HH ˆBˆÚ[™ÙWÜÞ\Ý[WÜ[]J]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—Kœ[]JNˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›YÚ žJH ËÈÚ[™ÙHYÚ\™XÝ[Ûˆ›ÜˆÙžÚY݈ˆYÚ žH]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›YÚ žˆYÚ žHH]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—K›YÚ žNˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝØÛÛÜŠH ËÈÚ[™ÙHÛÛ܈›ÜˆÙžÚY݈ˆÚYÝØÛÛ܈H]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝØÛÛ܎ˆYŠÚYÝØÛÛ܈OH LJBˆˆÚYÝØÛÛ܈H ˆBˆ[ÙHYŠÚYÝØÛÛ܈OH LŠBˆˆÚYÝØÛÛ܈H LNˆBˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝØ[JH ËÈÚ[™ÙHÛÛ܈›ÜˆÙžÚY݈ˆÚYÝØ[HH]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝØ[NˆYŠÚYÝØ[HOH“S‘ÓSÑWÓSÑS -BˆˆÚYÝØ[HH“S‘ÓSÑWÓ“Ó‘NˆBˆBˆ[ÙHYŠ]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝÛÜXÚ]JH ËÈÚ[™ÙHÛÛ܈›ÜˆÙžÚY݈ˆÚYÝÛÜXÚ]HH]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—KœÚYÝÛÜXÚ]NˆYŠÚYÝÛÜXÚ]HOH LJBˆˆÚYÝÛÜXÚ]HH ˆBˆYŠÚYÝÛÜXÚ]HOH LŠBˆˆÚYÝÛÜXÚ]HH LNˆBˆBˆ[ÙBˆˆÛX\�Ü]ÛŠ ›]™[ OœÜ]ÛœÚ[�ÖØÝ\œ™[�ÜÜ]Û—JNˆBˆ -ÊØÝ\œ™[�ÜÜ]ێˆBˆB‚ˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK™[� -BˆˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žˆ›JBˆˆ›HH^Y\–ÚWK™[� OœÜÚ][Û‹žˆBˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žJBˆˆHH^Y\–ÚWK™[� OœÜÚ][Û‹žˆBˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žˆˆ›JBˆˆ›HH^Y\–ÚWK™[� OœÜÚ][Û‹žŽÂˆBˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žˆJBˆˆHH^Y\–ÚWK™[� OœÜÚ][Û‹žŽÂˆBˆ�[\^JÊΈBˆB‚ˆYŠ]™[ O�ØZ][™ÊBˆˆ ËÈØZ]›Üˆ[[™[ZY\ÈÈ™HY™X]YˆYŠYš[™[� -TWÑS‘SVJJBˆˆ]™[ O�ØZ][™ÈH ˆYŠ]™[ O››Ü™\Ù]H JBˆˆ[Y[Y�H]™[ OœÙ][YH -ˆÛؘ[ØÛÛ™šY˘ÛÝ[�\—ÜÜYYÈ ËÈ™Xˆ �  Œ H H\È[™H[Ý™Y\™HÈÙ]Ý\ÝÛH[YBˆBˆÛ×Ý[YHHÝ[YH -È È -ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYYˆBˆB‚ˆYŠ�[\^HOH -Bˆˆ™]\›ŽÂˆB‚‚‚ˆYŠ[]™[ O�ØZ][™ÈÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÒSTPÐP“WÐPÕU‘JBˆˆYŠ]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÔ’QÒ -Bˆ‚ˆYØZ[œÝ[™H -]™[ O�ÚYHšY[Û[Ù\Ëš™\ÊN‚ˆYŠ›H HHˆšY[Û[Ù\Ëš™\ÊBˆˆÈHY˜[˜Ù^ˆBˆ[ÙBˆˆÈH -H -È›JH È ˆ HšY[Û[Ù\Ëš™\È È ˆ -È]™[ O˜Ø[Y\˜^Ù™œÙ]ˆB‚ˆYŠÈØÜ›ÛZ[ž -BˆˆÈHØÜ›ÛZ[žˆBˆ[ÙHYŠÈˆØÜ›ÛX^ -BˆˆÈHØÜ›ÛX^ˆB‚ˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈ›ØÚØYJBˆˆÈH›ØÚØYNˆB‚ˆYŠÈˆY˜[˜Ù^ -BˆˆYŠÈˆY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HΈBˆ[ÙHYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈY˜[˜Ù^ -BˆˆYŠÈY˜[˜Ù^ H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HΈB‚ˆYŠY˜[˜Ù^ -BˆˆY˜[˜Ù^H ˆBˆYŠY˜[˜Ù^�H]™[ O�ÚY HšY[Û[Ù\Ëš™\ÊBˆˆY˜[˜Ù^H -›Ø] -[]™[ O�ÚY HšY[Û[Ù\Ëš™\ΈYØZ[œÝ[™H NˆB‚ˆYŠYØZ[œÝ[™ -Bˆˆ]™[ OœÜÊÊΈBˆ[ÙBˆˆ]™[ OœÜÈH -[� -XY˜[˜Ù^ˆB‚‚ˆBˆ[ÙHYŠ]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -Bˆ‚ˆYØZ[œÝ[™H -]™[ O�ÚYHšY[Û[Ù\Ëš™\ÊN‚ˆYŠ›H HHˆšY[Û[Ù\Ëš™\ÊBˆˆÈHY˜[˜Ù^ˆBˆ[ÙBˆˆÈH -H -È›JH È ˆ HšY[Û[Ù\Ëš™\È È ˆ -È]™[ O˜Ø[Y\˜^Ù™œÙ]ˆB‚ˆYŠÈØÜ›ÛZ[ž -BˆˆÈHØÜ›ÛZ[žˆBˆ[ÙHYŠÈˆØÜ›ÛX^ -BˆˆÈHØÜ›ÛX^ˆB‚ˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆ]™[ O�ÚY HšY[Û[Ù\Ëš™\È HÈ›ØÚØYJBˆˆÈH]™[ O�ÚY HšY[Û[Ù\Ëš™\È H›ØÚØYNˆB‚ˆYŠÈY˜[˜Ù^ -BˆˆYŠÈY˜[˜Ù^ H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HΈBˆ[ÙHYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈˆY˜[˜Ù^ -BˆˆYŠÈˆY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HΈB‚ˆYŠY˜[˜Ù^ˆ]™[ O�ÚY HšY[Û[Ù\Ëš™\ÊBˆˆY˜[˜Ù^H -›Ø] -[]™[ O�ÚY HšY[Û[Ù\Ëš™\ΈBˆYŠY˜[˜Ù^H -BˆˆY˜[˜Ù^H ˆYØZ[œÝ[™H NˆB‚ˆYŠYØZ[œÝ[™ -Bˆˆ]™[ OœÜÊÊΈBˆ[ÙBˆˆ]™[ OœÜÈH -[� -J -]™[ O�ÚY HšY[Û[Ù\Ëš™\ÊH HY˜[˜Ù^ -NˆBˆBˆ[ÙHYŠ]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓÕUÐT‘ -H ËȈØÜ›ÛÛ›Bˆ‚ˆYŠ›H HHˆšY[Û[Ù\Ë�”™\ÊBˆˆÈHY˜[˜Ù^NˆBˆ[ÙBˆˆÈH -›H -ÈJH È ˆ HšY[Û[Ù\Ë�”™\È È ˆ -È]™[ O˜Ø[Y\˜^›Ù™œÙ]ˆB‚ˆYŠÈØÜ›ÛZ[žŠBˆˆÈHØÜ›ÛZ[žŽÂˆBˆ[ÙHYŠÈˆØÜ›ÛX^ŠBˆˆÈHØÜ›ÛX^ŽÂˆB‚ˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈ›ØÚØYJBˆˆÈH›ØÚØYNˆB‚ˆYŠÈˆY˜[˜Ù^JBˆˆYŠÈˆY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HHΈBˆ[ÙHYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈY˜[˜Ù^JBˆˆYŠÈY˜[˜Ù^H H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HHΈB‚ˆYŠY˜[˜Ù^Hˆ[™[ÚZYÚ HšY[Û[Ù\Ë�”™\ÊBˆˆY˜[˜Ù^HH -›Ø] -\[™[ÚZYÚ HšY[Û[Ù\Ë�”™\ΈYØZ[œÝ[™H NˆBˆYŠY˜[˜Ù^H -BˆˆY˜[˜Ù^HH ˆB‚ˆYŠYØZ[œÝ[™ -Bˆˆ]™[ OœÜÊÊΈBˆ[ÙBˆˆ]™[ OœÜÈH -[� -XY˜[˜Ù^NˆBˆBˆ[ÙHYŠ]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÒS•ÐT‘ -BˆˆYŠ›H HHˆšY[Û[Ù\Ë�”™\ÊBˆˆÈHY˜[˜Ù^NˆBˆ[ÙBˆˆÈH -›H -ÈJH È ˆ HšY[Û[Ù\Ë�”™\È È ˆ -È]™[ O˜Ø[Y\˜^›Ù™œÙ]ˆB‚ˆYŠÈØÜ›ÛZ[žŠBˆˆÈHØÜ›ÛZ[žŽÂˆBˆ[ÙHYŠÈˆØÜ›ÛX^ŠBˆˆÈHØÜ›ÛX^ŽÂˆB‚ˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆ[™[ÚZYÚ HšY[Û[Ù\Ë�”™\È HÈ›ØÚØYJBˆˆÈH[™[ÚZYÚ HšY[Û[Ù\Ë�”™\È H›ØÚØYNˆB‚ˆYŠÈY˜[˜Ù^JBˆˆYŠÈY˜[˜Ù^H H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HHΈBˆ[ÙHYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÐ�PÒÊH ‰ˆÈˆY˜[˜Ù^JBˆˆYŠÈˆY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HHΈB‚ˆYŠY˜[˜Ù^Hˆ[™[ÚZYÚ HšY[Û[Ù\Ë�”™\ÊBˆˆY˜[˜Ù^HH -›Ø] -\[™[ÚZYÚ HšY[Û[Ù\Ë�”™\ΈBˆYŠY˜[˜Ù^HH -BˆˆY˜[˜Ù^HH ˆYØZ[œÝ[™H NˆB‚ˆYŠYØZ[œÝ[™ -Bˆˆ]™[ OœÜÊÊΈBˆ[ÙBˆˆ]™[ OœÜÈH -[� -J -[™[ÚZYÚ HšY[Û[Ù\Ë�”™\ÊH HY˜[˜Ù^JNˆBˆBˆ ËÝ\ÝÛ‹[]˜]܈ÝYÙBˆ[ÙHYŠ]™[ OœØÜ›Û\ˆ ˆ -ÐÔ“ÓÕTÐÔ“ÓÑÕÓŠJBˆˆ ËØY˜[˜Ù^H -ÏH �NˆYŠØÜ›ÛYOH JBˆˆØÜ›ÛYH ˆY˜[˜Ù^JÊΈBˆ[ÙBˆˆØÜ›ÛY -ÊΈBˆ]™[ OœÜÈH -[� -XY˜[˜Ù^NˆBˆKËÚYŠ[]™[ O�ØZ][™ÊB‚ˆ ËȈ]]Ë\ØÜ›ÛˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓQ• -H -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÔ’QÒ -JH ËÈYYØÜ›Û\H›ÝÈÙZ\™[™ÜÈØ[ˆ\[‹�]Û›HYˆH[Ù\ˆ\È^žH[ˆ\Ú[™È›ØÚØY\ËÛˆ‚ˆYŠØ[Y\˜]\HOH JBˆˆ›HH NNNNNˆHHNNNNNNÈ ËÜ™XØ[Ý[]Bˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK™[� -BˆˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žˆ H^Y\–ÚWK™[� OœÜÚ][Û‹žHˆ›JBˆˆ›HH^Y\–ÚWK™[� OœÜÚ][Û‹žˆ H^Y\–ÚWK™[� OœÜÚ][Û‹žNˆBˆYŠ^Y\–ÚWK™[� OœÜÚ][Û‹žˆ H^Y\–ÚWK™[� OœÜÚ][Û‹žHJBˆˆHH^Y\–ÚWK™[� OœÜÚ][Û‹žˆ H^Y\–ÚWK™[� OœÜÚ][Û‹žNˆBˆBˆBˆBˆYŠ›H HHˆšY[Û[Ù\Ë�”™\ÊBˆˆÈHY˜[˜Ù^NˆBˆ[ÙBˆˆÈH -›H -ÈJH È ˆ HšY[Û[Ù\Ë�”™\È È ˆ -È]™[ O˜Ø[Y\˜^›Ù™œÙ]ˆB‚ˆ ËÈ™]ÈØÜ›Û[Z]ˆYŠÈˆØÜ›ÛX^ŠBˆˆÈHØÜ›ÛX^ŽÂˆBˆ[ÙHYŠÈØÜ›ÛZ[žŠBˆˆÈHØÜ›ÛZ[žŽÂˆB‚ˆYŠÈOHY˜[˜Ù^JBˆˆYŠÈˆY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H -È]™[ OœØÜ›ÛÜYYˆBˆ[ÙHYŠÈY˜[˜Ù^H H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^H H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^HH -›Ø] -]ΈB‚ˆYŠY˜[˜Ù^Hˆ[™[ÚZYÚ H -]™[ Oœ›ØÚÚ[™ÈÈ Mˆˆ LŠH HšY[Û[Ù\Ë�”™\ÊBˆˆY˜[˜Ù^HH -›Ø] -J[™[ÚZYÚ H -]™[ Oœ›ØÚÚ[™ÈÈ Mˆˆ LŠH HšY[Û[Ù\Ë�”™\ÊNˆBˆYŠY˜[˜Ù^H -BˆˆY˜[˜Ù^HH ˆBˆBˆ ËÈ›ÝÈ]]ÈØÜ›Ûˆ[ÙHYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÒS•ÐT‘ -H -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÓÕUÐT‘ -JBˆˆYŠ›H HHˆšY[Û[Ù\Ëš™\ÊBˆˆÈHY˜[˜Ù^ˆBˆ[ÙBˆˆÈH -H -È›JH È ˆ HšY[Û[Ù\Ëš™\È È ˆ -È]™[ O˜Ø[Y\˜^Ù™œÙ]ˆB‚ˆ ËÈ™]ÈØÜ›Û[Z]ˆYŠÈˆØÜ›ÛX^ -BˆˆÈHØÜ›ÛX^ˆBˆ[ÙHYŠÈØÜ›ÛZ[ž -BˆˆÈHØÜ›ÛZ[žˆB‚ˆYŠÈOHY˜[˜Ù^ -BˆˆYŠÈˆY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ -È]™[ OœØÜ›ÛÜYYˆBˆ[ÙHYŠÈY˜[˜Ù^ H]™[ OœØÜ›ÛÜYY -BˆˆÈHY˜[˜Ù^ H]™[ OœØÜ›ÛÜYYˆBˆY˜[˜Ù^H -›Ø] -]ΈB‚ˆYŠY˜[˜Ù^ˆ]™[ O�ÚY HšY[Û[Ù\Ëš™\ÊBˆˆY˜[˜Ù^H -›Ø] -J]™[ O�ÚY HšY[Û[Ù\Ëš™\ÊNˆBˆYŠY˜[˜Ù^ -BˆˆY˜[˜Ù^H ˆBˆBˆ ËÙ[™Ùˆˆ]]Ë\ØÜ›Ûˆ ËÈÛؘ[˜[YH›Üˆ\WÜ[™[ˆØÜ›ÛHY˜[˜Ù^ HˆØÜ›ÛHHY˜[˜Ù^H HNŸB‚‚�›ÚY\]WÜØÜ›ÛYØ™Ê -BžÂˆ›Ø]›ØÚݘ]™[ˆ[œÚYÛ™YÚ\ˆ™[ÛœÌÌ—NËËÌÊŽˆÝ]XÈ[�›ØÚÜÜÈH ˆÝ]XÈ[�›ØÚÛÙ™œÜÚ[™VÌÌ—HBˆˆ ‹ ‹ Ë  K ‹ Ë Ëˆ  KKKK  ˆ Ë Ë ‹ K  Ë ‹ ‹ˆ K K     K BˆNÈ ËțܛX[›ØÚˆÝ]XÈ[�›ØÚÛÙ™œÜÚZÙVÌÌ—HBˆˆ ‹ ‹ ‹ ‹ ‹ ‹ ‹ ‹ˆ ‹ ‹   ‹   ‹ˆ ‹ ‹ ‹ ‹ ‹ ‹ ‹ ‹ˆ ‹ ‹   ‹   ‹ˆNÈ ËÈÛÝËÛÛœÝ[�˜\œš[™È›ØÚËZÙHÛˆH˜Z[‚ˆÝ]XÈ[�›ØÚÛÙ™œÜ�[X›VÌÌ—HBˆˆ ‹ ‹ Ë Ë ‹ ‹ Ë Ëˆ ‹ ‹ Ë Ë ‹ Ë ‹ ˈ ‹ ‹ Ë Ë ‹ ‹ Ë Ëˆ ‹ ‹ Ë Ë ‹ Ë ‹ ˈNÈ ËȘ\Ý ÛÛœÝ[��[X›[™ËZÙH[‹ÛÛˆH˜[ˆ܈˜Z[\‚ˆ[�ˆH^[ž]\ÖÊ[� -TVSÌÌ—N‚ˆ ËÈ[YHÈ\]H™[Ûˆ[™ØÜ™Y[ˆ[›YȘ[ÙOˆYŠÝ[YH�H™[Û—Ý[YH ‰ˆYœ™Y^™X[ -BˆˆY[XÜJ™[Ûœ ™[Û�X›H -È LŽ -ˆ‹ -ˆŠNˆY[XÜJ™[Û�X›H -È LŽ -ˆ‹™[Ûœ -È ˆ -ˆ‹ ˆ -ˆŠNˆY[XÜJ™[Û�X›H -È - LŽ -È ŠJœ‹™[Ûœ  ˆ -ˆŠN‚ˆ™[Û—Ý[YHHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È ÊNˆB‚ˆYŠYœ™Y^™X[ -Bˆˆ›ØÚݘ]™[H -]™[ Oœ›ØÚÚ[™ÊHÈ - -Ý[YH H˜]™[[YJH È - -›Ø] -YÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY È Ì -JHˆ È ËÈ›ÈZÙH[ˆ™X[Y™KX^X™BˆYŠ]™[ O˜™ÜÜYY -Bˆˆ›ØÚݘ]™[H \›ØÚݘ]™[ˆBˆ™Ý˜]™[Y -ÏH -Ý[YH H˜]™[[YJH -ˆ]™[ O˜™ÜÜYY È Ì -ˆ  -È›ØÚݘ]™[ˆ˜™Ý˜]™[Y -ÏH -Ý[YH H˜]™[[YJH -ˆ]™[ O�˜™ÜÜYY È Ì -ˆ ˆBˆ[ÙBˆˆ^[YH -ÏHÝ[YH H˜]™[[YNˆB‚ˆ[Y]˜\ˆHÝ[YH H^[YN‚ˆYŠ]™[ Oœ›ØÚÚ[™ÊBˆˆ›ØÚÜÜÈH -[Y]˜\ˆ È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È -JH ˆ ÌNˆYŠ]™[ Oœ›ØÚÚ[™ÈOH JBˆˆÙžÞWÛÙ™œÙ]H]™[ Oœ]XZÙH H  H›ØÚÛÙ™œÜÚ[™VÜ›ØÚÜÜ×NˆBˆ[ÙHYŠ]™[ Oœ›ØÚÚ[™ÈOH ŠBˆˆÙžÞWÛÙ™œÙ]H]™[ Oœ]XZÙH H  H›ØÚÛÙ™œÜÚZÙVÜ›ØÚÜÜ×NˆBˆ[ÙHYŠ]™[ Oœ›ØÚÚ[™ÈOH ÊBˆˆÙžÞWÛÙ™œÙ]H]™[ Oœ]XZÙH H  H›ØÚÛÙ™œÜ�[X›VÜ›ØÚÜÜ×NˆBˆBˆ[ÙBˆˆYŠ]™[ Oœ]XZÙH�H -BˆˆÙžÞWÛÙ™œÙ]H]™[ Oœ]XZÙH H ˆBˆ[ÙBˆˆÙžÞWÛÙ™œÙ]H]™[ Oœ]XZÙH -È ˆBˆB‚ˆ ËÚYŠ]™[ OœØÜ›Û\ˆOTÐÔ“ÓÕT ‰ˆ]™[ OœØÜ›Û\ˆOTÐÔ“ÓÑÕÓŠHÙžÞWÛÙ™œÙ] OHY˜[˜Ù^NˆÙžÞWÛÙ™œÙ] -ÏHÙžÞWÛÙ™œÙ]ØYŽÈ ËÌŒ LWÌ Ì ËΈ\H[Ù\ˆY�\ÝY[� ‚‚ˆ˜]™[[YHHÝ[YN‚ˆYŠÝ[YH�H]™[ Oœ]XZÙ][YJBˆˆ]™[ Oœ]XZÙH ÏH ŽÂˆ]™[ Oœ]XZÙ][YHHÝ[YH -È -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È �JNˆBŸB‚�›ÚY˜]×ÜØÜ›ÛYØ™Ê -BžÂˆ[�[™^H  ‹HH ‹ Nˆ×Û^Y\ˆ -›^Y\ŽÂˆ[�ÚY ZYÚ‚ˆ[�œ œKœËœ‚ˆYŠšY]ÜÜ�Ȉ -BˆˆœHšY]ÜÜ�ˆœHHšY]ÜÜ�NˆœÈHšY]ÜÜ�ΈœHšY]ÜÜ�ˆBˆ[ÙBˆˆœHœHH ˆœÈHšY[Û[Ù\Ëš™\ΈœHšY[Û[Ù\Ë�”™\ΈB‚ˆ ËÚYŠ]™[ -Hš[�ЉY Y Y Yˆ‹œ œKœËœ -N‚ˆ×Ù˜]ÛY]ÙØÜ™Y[›Y]ÙHZ[›Y]Ù‚ˆ Êœ×Ù˜]ÛY]Ù -œØÜ™Y[›Y]ÙH œØÜ™Y[›Y]Ùˆ›ÜŠHH ÈH]™[ O›�[ZÛ\ÎÈJÊÊBˆˆÜš]\WØYÜÜš]J -[� -J]™[ OšÛ\ÖÚWKž HØÜ™Y[ž -ÈÙžÞÛÙ™œÙ] -K -[� -J]™[ OšÛ\ÖÚWKžˆ H]™[ OšÛ\ÖÚWK™\ HØÜ™Y[žH -ÈÙžÞWÛÙ™œÙ] -KÓWÖ‹Û\Üš]KØÜ™Y[›Y]Ù  -NˆJ‹Â‚ˆ›ÜŠ[™^H È[™^]™[ O›�[[^Y\œÜ™YŽÈ[™^ -ÊÊBˆˆ^Y\ˆH]™[ O›^Y\œÜ™Yˆ -È[™^‚ˆØÜ™Y[›Y]ÙH^Y\‹O™˜]ÛY]Ù‚ˆ ËÜš[�Š›^Y\ˆ Y [™N‰]KމYˆ‹[™^ ^Y\‹O™Ùž š[™K^Y\‹OœÜÚ][Û‹žŠN‚‚BKËÈ^Y\ˆ]\Ý™H[˜X›Y[™]™H]X\ÝÛ™H[œÝXÙK܈ÙHۉݘ]È] ‚ˆYŠ\ØÜ™Y[›Y]Ù ž™\X]\ØÜ™Y[›Y]Ù ž\™\X][^Y\‹O™[˜X›Y -BˆˆÛÛ�[�YNˆB‚ˆÚYHØÜ™Y[›Y]Ù žÜ[ˆH^Y\‹OœÚ^™Kž -È^Y\‹OœÜXÚ[™ËžˆZYÚHØÜ™Y[›Y]Ù ž\Ü[ˆH^Y\‹OœÚ^™KžH -È^Y\‹OœÜXÚ[™ËžŽÂ‚ˆH -[� -J^Y\‹O›Ù™œÙ] ž H -Y˜[˜Ù^ -șݘ]™[Y -ˆ^Y\‹O˜™ÜÜYY˜][ÊH -ˆ - KŒ H^Y\‹Oœ˜][Ëž -H -H‚ˆ ËÜš[�Š›^Y\ž˜][È Yˆ Y Y—ˆ‹^Y\‹Ož˜][Ë ^Y\‹O˜™ÜÜYY˜][ÊN‚ˆYŠ -]™[ OœØÜ›Û\ˆ ˆÐÔ“ÓÕT -JBˆˆ ËÞˆH -[� -J^Y\‹O›Ù™œÙ] žˆ -ÈY˜[˜Ù^H -ˆ - KŒ H^Y\‹Oœ˜][ËžŠH -HˆˆH -[� -J^Y\‹O›Ù™œÙ] žˆ H -Y˜[˜Ù^H -Ș™Ý˜]™[Y -ˆ^Y\‹O˜™ÜÜYY˜][ÊH -ˆ - KŒ H^Y\‹Oœ˜][ËžŠH -HˆBˆ[ÙBˆˆ ËÞˆH -[� -J^Y\‹O›Ù™œÙ] žˆ HY˜[˜Ù^H -ˆ - KŒ H^Y\‹Oœ˜][ËžŠH -HˆˆH -[� -J^Y\‹O›Ù™œÙ] žˆ H -Y˜[˜Ù^H -Ș™Ý˜]™[Y -ˆ^Y\‹O˜™ÜÜYY˜][ÊH -ˆ - KŒ H^Y\‹Oœ˜][ËžŠH -HˆB‚ˆYŠ^Y\‹Oœ]XZÙJBˆˆ -ÏHÙžÞÛÙ™œÙ]ˆˆ -ÏHÙžÞWÛÙ™œÙ]ˆ ËÜš[�ЉYH Y Yˆ‹[™^ ÙžÞWÛÙ™œÙ] ŠNˆB‚ˆ OHœˆˆ OHœN‚‚ˆYŠ -BˆˆHH - ^ -H ÈÚYˆ OHÚYˆBˆ[ÙBˆˆHH ˆB‚ˆYŠHˆ  ‰ˆØÜ™Y[›Y]Ù �Ø]\‹�Ø]\›[ÙHOHÐUT—ÓSÑWÔÒPTˆ ‰ˆØÜ™Y[›Y]Ù �Ø]\‹˜[\]YJBˆˆKKNˆ OHÚYˆB‚ˆYŠˆ -BˆˆˆH - ^ŠH ÈZYÚˆˆ OHZYÚˆBˆ[ÙBˆˆˆH ˆBˆ‚BZYŠ^Y\‹O›™[ÛŠBˆˆØÜ™Y[›Y]Ù �X›HH™[Û�X›NˆBˆ[ÙBˆˆYŠÝ\œ™[�Ü[]Hˆ -BˆˆØÜ™Y[›Y]Ù �X›HH]™[ Oœ[]\ÖØÝ\œ™[�Ü[]H H WNˆBˆ[ÙBˆˆØÜ™Y[›Y]Ù �X›HH•SˆBˆB‚ˆØÜ™Y[›Y]Ù �Ø]\‹�Ø]™][YHH -[� -J[Y]˜\ˆ -ˆØÜ™Y[›Y]Ù �Ø]\‹�Ø]™\ÜYY -NˆØÜ™Y[›Y]Ù ž™\X]HØÜ™Y[›Y]Ù ž\™\X]H ˆ›ÜŠHHŽÈˆ^Y\‹O™˜]ÛY]Ù ž\™\X] ‰ˆHœÈH -ÏHZYÚ ŠÊËØÜ™Y[›Y]Ù ž\™\X] -ÊÊNˆ›ÜŠHÈH^Y\‹O™˜]ÛY]Ù ž™\X] ‰ˆœÈ -È -ØÜ™Y[›Y]Ù �Ø]\‹�Ø]\›[ÙHOHÐUT—ÓSÑWÔÒPTˆÈ ˆØÜ™Y[›Y]Ù �Ø]\‹˜[\]YH -ˆ ŠNÈ -ÏHÚY JÊËØÜ™Y[›Y]Ù ž™\X] -ÊÊN‚ˆYŠ^Y\‹O™Ùž œØÜ™Y[‹O›XYÚXÈOHØÜ™Y[—ÛXYÚXÊBˆˆÜš]\WØYÜØÜ™Y[Š -Èœ ˆ -ÈœK^Y\‹Ož‹^Y\‹O™Ùž œØÜ™Y[‹ œØÜ™Y[›Y]Ù [™^ -NˆBˆ[ÙHYŠ^Y\‹O™Ùž œÜš]KO›XYÚXÈOHÜš]WÛXYÚXÊBˆˆÜš]\WØYÙœ˜[YJ -Èœ ˆ -ÈœK^Y\‹Ož‹^Y\‹O™Ùž œÜš]K œØÜ™Y[›Y]Ù [™^ -NˆB‚ˆ ËÜš[�ŠŠŠŠŠŠŠ‰Y Y Y Y Y -ŠŠŠŠ—ˆ‹ -Ýœ ŠÝœK^Y\‹O‹ž‹ØÜ™Y[›Y]Ù ž™\X] ØÜ™Y[›Y]Ù ž\™\X] -NˆB‚‚ŸB‚�L̈Ù][�\�˜[ - -BžÂˆ[�\�˜[H[Y\—ÙÙ][�\�˜[ -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY -NÈ ËÈÛÈ[�\�˜[Ø[ˆ™HÙÙÙY[�È[ÝšYBˆYŠ[�\�˜[ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -Bˆˆ[�\�˜[H NˆBˆYŠ[�\�˜[ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY È -Bˆˆ[�\�˜[HÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY È ˆBˆ™]\›ˆ[�\�˜[ŸB‚‹ËÈØ\ÚÙ^K[[Ûˆ‹‚‹ËÈ Œ NKL LŒ‚‹ËÈ‹ËÈ�[ˆ[œ]ØÜš\ˈÚ[Z[\ˆÈÙ^\Ë�]‹ËÈ^XÝ]H™Y›Ü™H›ØÙ\ÜÚ[™È[žHÛÛ[X[™�[˜Ý[ۜ˂�›ÚY^XÝ]WÚ[œ]ÜØÜš\Ê[�^Y\—Ú[™^ -BžÂ‚\×Ü^Y\ˆ -œ^Y\—ÛØšˆH•S‚BB‚\^Y\—ÛØšˆH^Y\ˆ -È^Y\—Ú[™^‚‚ZYˆ -\^Y\—ÛØšŠB‚^‚B\™]\›ŽÂ‚_B‚B‚ZYˆ -^Y\—ÛØš‹O›™]ÚÙ^\È -Ù^\ØÜš\˜]H ‰ˆ^Y\‹OšÙ^\ÊH^Y\‹Oœ™[X\ÙZÙ^\ÊB‚^‚BKËÈ Œ NKL LŒˆÛ‰Ý^\ÝY]‚BKËÚYˆ -]™[ -B‚BKËÞ‚‚BBKËÙ^XÝ]WÛ]™[ÚÙ^WÜØÜš\ -^Y\—Ú[™^ ^Y\ŠN‚BBKËÙ^XÝ]WÙ[�]WÚÙ^WÜØÜš\ -^Y\‹™[� -N‚BKËßB‚BKËÙ^XÝ]WÚÙ^WÜØÜš\ -^Y\—Ú[™^ ^Y\ŠN‚‚BY^XÝ]WÚ[œ]ÜØÜš\Ø[ -^Y\—Ú[™^ -N‚_BŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMŠ‚ŠˆY\™XÝ[Û‹\™[]]™HÛÛ[X[™›YÜÈÈH\ÚXØ[Šˆ[œ]X\ÚË‚Š‚ŠˆY�[™šYÚ™[XZ[ˆ[ˆH™\Ý[ÛÈ\™ XÛÙYŠˆÛÛ[X[™ÈØ[ˆÛÛ�[�YHÈ[œÜXÝH\ÚXØ[[œ] ‚Š‹ÂœÝ]XÈÙ^WÛX\Ú×ÝÛÛ[X[™Ú[œ]Ü™\ÛÛ™WÙ\™XÝ[ÛŠˆÛÛœÝÙ^WÛX\Ú×Ý[œ]Ù›YÜˈÛÛœÝ[�]JˆXÝ[™×Ù[�]BŠHˆÙ^WÛX\Ú×Ý™\ÛÛ™YÙ›YÜÈH[œ]Ù›YÜ΂ˆYŠXXÝ[™×Ù[�]JHˆ™]\›ˆ™\ÛÛ™YÙ›YÜΈB‚ˆYŠ[œ]Ù›YÜÈ ˆ“Q×ÓSÕ‘SQ• -Hˆ™\ÛÛ™YÙ›YÜÈHXÝ[™×Ù[�]KO™\™XÝ[Û‚ˆÈ“Q×Ð�PÒÕÐT‘ˆˆ“Q×ѓԕÐT‘‚ˆH[ÙHYŠ[œ]Ù›YÜÈ ˆ“Q×ÓSÕ‘T’QÒ -Hˆ™\ÛÛ™YÙ›YÜÈHXÝ[™×Ù[�]KO™\™XÝ[Û‚ˆÈ“Q×ѓԕÐT‘ˆˆ“Q×Ð�PÒÕÐT‘ˆB‚ˆ™]\›ˆ™\ÛÛ™YÙ›YÜΟB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËLMÈ HÜ™Ú[˜[žH][›™[Ë[šÛ›ÝÈ]K‚Š‚Šˆ\]HH[œ]Ý]H›ÜˆXXÚ^Y\‹ˆ\È[˜ÛY\Šˆ\ÚXØ[Ù^\ËÛÛ[X[™[œ]\ÝÜžK[™]]ÛX]XŠˆ[YÙ\ˈ™Y˜XÝÜ™YÈÝ\Ü�\™XÝ[Û‹\™[]]™HŠˆÛÛ[X[™›YÜÈ[™^[œÚX›HÛÛ[X[™Þ\Ý[K‚Š‹Â�›ÚY[œ]™Yœ™\Ú -[�^\™XÛ[ÙJH‚ˆ[�ˆ×Ü^Y\ˆ -˜XÝ[™×Ü^Y\ŽÂ‚ˆÛÛ�›ÛÝ\]J^Y\˜ÛÛ�›ÛÚ[�\œËPVÔVQT”ÊN‚ˆ›ÝÙ^\ÈH ˆ›Ý™]ÚÙ^\ÈH ‚ˆ›ÜŠH ÈPVÔVQT”ÎÈ -ÊÊBˆˆXÝ[™×Ü^Y\ˆH^Y\ˆ -È‚ˆYˆ -^\™XÛ[ÙHOHWÔ‘P×ÔVJH‚ˆXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\ÈH -^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜÈXÝ[™×Ü^Y\‹OšÙ^\ÊH H^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜΈXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\È �H˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹OšÙ^\ÈH^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜÈ ˆ˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹O›™]ÚÙ^\ÈH^Y\˜ÛÛ�›ÛÚ[�\œÖÜKO›™]ÚÙ^Y›YÜÈ ˆ˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\ÈHXÝ[™×Ü^Y\‹O›™]ÚÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HXÝ[™×Ü^Y\‹OšÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈˆH[ÙHˆ ËÈ[ˆ^H[ÙNˆY™\ÜÙYÙ^\ÈÈ™XÈÙ^\ˆXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\ÈH -^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜÈXÝ[™×Ü^Y\‹Oœ™]šÙ^\ÊH H^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜΈXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\È �H˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹OšÙ^\ÈH^Y\˜ÛÛ�›ÛÚ[�\œÖÜKOšÙ^Y›YÜÈ ˆ˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹O›™]ÚÙ^\ÈH^Y\˜ÛÛ�›ÛÚ[�\œÖÜKO›™]ÚÙ^Y›YÜÈ ˆ˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\ÈHXÝ[™×Ü^Y\‹O›™]ÚÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �HXÝ[™×Ü^Y\‹OšÙ^\ΈXÝ[™×Ü^Y\‹Oœ^ZÙ^\È �H˜XÝ[™×Ü^Y\‹O™\ØX›ZÙ^\ΈB‚BBBB‚BY^XÝ]WÚ[œ]ÜØÜš\Ê -NÂBB‚ˆ Ê‚ˆ -ˆ™\Ù]ÛÛ[X[™Ù\]Y[˜ÙHYˆH\ÝÛÛ[X[™Ø\ˆ -ˆÛÈÛ™ÈYÛË‚ˆ -‹ÂˆYŠXÝ[™×Ü^Y\‹O™[� ‰ˆXÝ[™×Ü^Y\‹O™[� O˜ÛÛ[X[™Ý[YHÝ[YJHˆY[\Ù] -ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžKˆ ˆÚ^™[ÙŠXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú\ÝÜžJBˆ -N‚ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]ØÛÝ[�H ˆXÝ[™×Ü^Y\‹O˜ÛÛ[X[™Ú[œ]Ú[™^H ˆB‚ˆ Ê‚ˆ -ˆ�Z[Û™H]™[�]™\žH™Yœ™\ÚÛÈ]]ÛX]XÈ[ˆ -ˆ™\ÚÛÈØ[ˆÜ™X]H[ˆYÙHÚ]Ý]H\ÚXØ[ˆ -ˆ�]ÛˆÚ[™ÙKˆÝÜ™H]Û›HÚ[ˆ]X\ÝÛ™Bˆ -ˆ™\ÜË™[X\ÙK܈]]ÛX]XÈ[YÙH^\ÝË‚ˆ -‹ÂˆˆרÛÛ[X[™Ú[œ]Ù]™[�[œ]Ù]™[�HÌN‚ˆ[œ]Ù]™[� œ™\ÜÈHÛÛ[X[™Ú[œ]Ü™\ÛÛ™WÙ\™XÝ[ÛŠˆXÝ[™×Ü^Y\‹O›™]ÚÙ^\ˈXÝ[™×Ü^Y\‹O™[�ˆ -N‚ˆ[œ]Ù]™[� œ™[X\ÙHHÛÛ[X[™Ú[œ]Ü™\ÛÛ™WÙ\™XÝ[ÛŠˆXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\ˈXÝ[™×Ü^Y\‹O™[�ˆ -N‚ˆ Ê‚ˆ -ˆ™[X\ÙYÙ^\ÈÙ\™H[[[YYX][H™Y›Ü™H\ˆ -ˆYÙKˆ[˜ÛYH[H[ˆH]™[�Û˜\ÚÝÛÈBˆ -ˆÛÛ[X[™ÝXÚ\ÈVÍLH -ȘHØ[ˆ\ݛݘXÝË‚ˆ -‹Âˆ[œ]Ù]™[� š[HÛÛ[X[™Ú[œ]Ü™\ÛÛ™WÙ\™XÝ[ÛŠˆXÝ[™×Ü^Y\‹OšÙ^\ˆXÝ[™×Ü^Y\‹Oœ™[X\ÙZÙ^\ˈXÝ[™×Ü^Y\‹O™[�ˆ -N‚ˆ[œ]Ù]™[� �[YHHÝ[YN‚ˆÛÛ[X[™Ú[œ]ÚÛÜÝ\�Ý\]JˆXÝ[™×Ü^Y\‹ˆ[œ]Ù]™[� œ™\Üˈ[œ]Ù]™[� �[YBˆ -N‚ˆYŠ[œ]Ù]™[� œ™\ÜÊHˆ Ê‚ˆ -ˆØ\\™H[œ]ÈÝ[[]\ÈÜÚ]]™Bˆ -ˆYÙKˆHÛÛ™šYÝ\˜X›HÛÛ[X[™X]Ú\‚ˆ -ˆ\Y\ÈXXÚÝ\ ÜÈÚÜ™Ý[YHÈZ\‚ˆ -ˆ™XÛÜ™Y™\ÜÈ[Y\ˈ\™ XÛÙYÛÛ[X[™ˆ -ˆÛÛ�[�YHÈ[œÜXÝ[œ]Ù]™[� œ™\ÜÈÛ›K‚ˆ -‹Âˆ[œ]Ù]™[� œ™\ÜרÚÜ™BˆÛÛ[X[™Ú[œ]Ü™\ÛÛ™WÙ\™XÝ[ÛŠˆXÝ[™×Ü^Y\‹OšÙ^\ˈXÝ[™×Ü^Y\‹O™[�ˆ -NˆB‚ˆ[œ]Ù]™[� šÛBˆÛÛ[X[™Ú[œ]ÚÛÝšYÙÙ\—ØÛÛXÝ -ˆXÝ[™×Ü^Y\‹ˆ[œ]Ù]™[� š[ ˆ[œ]Ù]™[� �[YBˆ -N‚ˆYŠ[œ]Ù]™[� œ™\܈[œ]Ù]™[� šÛˆ[œ]Ù]™[� œ™[X\ÙJH‚ˆ[œ]Ù]™[� �XÚÜÈH[Y\—ÙÙ]XÚÊ -NˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÜ\Ú -XÝ[™×Ü^Y\‹ š[œ]Ù]™[� -N‚ˆYŠXÝ[™×Ü^Y\‹O™[� -HˆXÝ[™×Ü^Y\‹O™[� O˜ÛÛ[X[™Ý[YHBˆÛÛ[X[™Ú[œ]Ú\ÝÜžWÙ^\˜][Û—Ý[YWÙÙ] -ˆXÝ[™×Ü^Y\‹O™[� ˆ[œ]Ù]™[� �[YBˆ -NˆBˆB‚ˆB‚ˆ›ÝÙ^\ÈHXÝ[™×Ü^Y\‹OšÙ^\Έ›Ý™]ÚÙ^\ÈHXÝ[™×Ü^Y\‹O›™]ÚÙ^\ΈˆB‚ŸB‚�›ÚY^XÝ]WÚÙ^\ØÜš\Ê -BžÂˆ[�ˆ›ÜŠH È]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈ -ÊÊBˆˆYŠWÜ]\ÙH ‰ˆ -]™[ÚXÚ×Ú[—ÜØÜ™Y[Š -JH ‰ˆ -^Y\–ÜK›™]ÚÙ^\È -Ù^\ØÜš\˜]H ‰ˆ^Y\–ÜKšÙ^\ÊH^Y\–ÜKœ™[X\ÙZÙ^\ÊJBˆˆYŠ]™[ -Bˆˆ^XÝ]WÛ]™[ÚÙ^WÜØÜš\ - -Nˆ^XÝ]WÙ[�]WÚÙ^WÜØÜš\ -^Y\–ÜK™[� -NˆBˆ^XÝ]WÚÙ^WÜØÜš\ - -Nˆ^XÝ]WÚÙ^WÜØÜš\Ø[ - -NˆBˆBŸB‚�›ÚY^XÝ]WÝ\]\ØÜš\Ê -BžÂˆYŠØÜš\Ò\Ò[š]X[^™Y - �\]WÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J Š\]WÜØÜš\ -JNˆBˆYŠ]™[ ‰ˆØÜš\Ò\Ò[š]X[^™Y - Š]™[ O�\]WÜØÜš\ -JJBˆˆØÜš\Ñ^XÝ]J Š]™[ O�\]WÜØÜš\ -JNˆBŸB‚�›ÚY^XÝ]WÝ\]YØÜš\Ê -BžÂˆYŠØÜš\Ò\Ò[š]X[^™Y - �\]YÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J Š\]YÜØÜš\ -JNˆBˆYŠ]™[ ‰ˆØÜš\Ò\Ò[š]X[^™Y - Š]™[ O�\]YÜØÜš\ -JJBˆˆØÜš\Ñ^XÝ]J Š]™[ O�\]YÜØÜš\ -JNˆBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L LŒBŠ‚Šˆ^XÝ]HÛؘ[[™]™[ØÜš\È[[YYX][H™Y›Ü™H[‚Šˆ[™Ú[™HÙÚXØ[ÛØÚÈXÚÈ\È›ØÙ\ÜÙY ‚Š‹Â�›ÚY^XÝ]WÝ\][ÙÚXÜØÜš\Ê -BžÂˆYŠØÜš\Ò\Ò[š]X[^™Y - �\]WÛÙÚX×ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J �\]WÛÙÚX×ÜØÜš\ -NˆBˆYŠ]™[ ‰ˆØÜš\Ò\Ò[š]X[^™Y - ›]™[ O�\]WÛÙÚX×ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J ›]™[ O�\]WÛÙÚX×ÜØÜš\ -NˆBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L LŒBŠ‚Šˆ^XÝ]HÛؘ[[™]™[ØÜš\ÈY�\ˆ[ˆ[™Ú[™HÙÚXØ[ŠˆÛØÚÈXÚÈ\È›ØÙ\ÜÙY ™Y›Ü™H]ÈÛØÚȘ[YHY˜[˜Ù\Ë‚Š‹Â�›ÚY^XÝ]WÝ\]YÙÚXÜØÜš\Ê -BžÂˆYŠØÜš\Ò\Ò[š]X[^™Y - �\]YÛÙÚX×ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J �\]YÛÙÚX×ÜØÜš\ -NˆBˆYŠ]™[ ‰ˆØÜš\Ò\Ò[š]X[^™Y - ›]™[ O�\]YÛÙÚX×ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J ›]™[ O�\]YÛÙÚX×ÜØÜš\ -NˆBŸB‚�›ÚY˜]×Ý^ØšœÊ -BžÂˆ[�Nˆ×Ý^Øšˆ -�^ØšŽÂˆYŠ[]™[ -Bˆˆ™]\›ŽÂˆBˆ›ÜŠHH ÈH]™[ O›�[]^ØšœÈÈJÊÊBˆˆ^ØšˆH]™[ O�^ØšœÈ -ÈN‚ˆYŠ^Øš‹O�[YH ‰ˆ^Øš‹O�[YHHÝ[YJBBKËÒYˆH[YHØ\ÈÙ][™\ÜÙY ™[[Ý™HH^Øš™XÝ ‚ˆˆ]™[ O�^ØšœÖÚWK�[YBOH ˆ]™[ O�^ØšœÖÚWKœÜÚ][Û‹žH ˆ]™[ O�^ØšœÖÚWKœÜÚ][Û‹žHH ˆ]™[ O�^ØšœÖÚWK™›Û�H ˆ]™[ O�^ØšœÖÚWKœÜÚ][Û‹žˆH ˆYŠ]™[ O�^ØšœÖÚWK�^ -Bˆˆœ™YJ]™[ O�^ØšœÖÚWK�^ -Nˆ]™[ O�^ØšœÖÚWK�^H•SˆBˆBˆ[ÙBˆˆYŠ^Øš‹O�^ -Bˆˆ›Û�Üš[�Û[™Ý -ˆ^Øš‹OœÜÚ][Û‹ž ˆ^Øš‹OœÜÚ][Û‹žKˆ^Øš‹O™›Û� ˆ^Øš‹OœÜÚ][Û‹ž‹ˆ^Øš‹O�^ ˆÝ›[Š^Øš‹O�^ -Bˆ -NˆBˆBˆBŸB‚š[�™XÛÜ™[œ]Ê -BžÂˆ[�H ˆ™XÒÙ^\È™XÚÙ^NˆZ[� �ÝÚ[™ÝÈH MŽÂˆL̈X^Ü™X×Ý[YHHÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -�Œ -ŒLÈ ËÈ›ÝXÝ[Û‚‚ˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×Ô‘PÊH™]\›ˆ ˆYˆ -\^\™XÜÝ]\ËO˜™YÚ[ˆ -Bˆˆ^\™XÜÝ]\ËOœÝ\�[YHHÝ[YNˆ^\™XÜÝ]\ËOœÞ[˜Ý[YHH ˆYˆ -^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆœ™YJ^\™XÜÝ]\ËO˜�Y™™\ŠNˆ^\™XÜÝ]\ËO˜�Y™™\ˆH•SˆBˆ^\™XÜÝ]\ËO˜�Y™™\ˆH -™XÒÙ^\ÊŠXØ[ØÊÚ[™ÝÊÜ^\™XÜÝ]\ËOœÞ[˜Ý[YJœÚ^™[ÙŠ™XÒÙ^\ÊKÚ^™[ÙŠ™XÒÙ^\ÊJNˆYˆ -^\™XÜÝ]\ËO˜�Y™™\ˆOH•S -Bˆˆš[�Š‘\œ›ÜˆÈ[ØØ]H�Y™™\ˆ[ˆ™XÛÜ™[œ]È[ÙK—ˆŠNˆ™]\›ˆ ˆBˆ^\™XÜÝ]\ËO˜™YÚ[ˆH Nˆ^\™XÜÝ]\ËOœÙYYHÙ]ÙYY - -Nˆ^\™XÜÝ]\ËO˜ÜÙYYHÝ[YNˆܘ[™ -Ý[YJNˆ^\™XÜÝ]\ËO�XÚÜÈH[Y\—ÙÙ]XÚÊ -NˆBˆ[ÙBˆˆYˆ -^\™XÜÝ]\ËOœÞ[˜Ý[YI]Ú[™ÝÈ�HÚ[™ÝËLˆ -H ËÈ\Ý\È•Sž]\ˆˆ^\™XÜÝ]\ËO˜�Y™™\ˆH -™XÒÙ^\ÊŠ\™X[ØÊ^\™XÜÝ]\ËO˜�Y™™\‹Ú^™[ÙŠ™XÒÙ^\ÊJŠ -^\™XÜÝ]\ËOœÞ[˜Ý[YKÝÚ[™ÝÊÌJJ�Ú[™ÝÊÝÚ[™ÝÊJNˆYˆ -^\™XÜÝ]\ËO˜�Y™™\ˆOH•S -Bˆˆš[�Š‘\œ›ÜˆÈ[ØØ]H�Y™™\ˆ[ˆ™XÛÜ™[œ]È[ÙK—ˆŠNˆ™]\›ˆ ˆH[ÙHY[\Ù] -^\™XÜÝ]\ËO˜�Y™™\ŠÊ^\™XÜÝ]\ËOœÞ[˜Ý[YJÌJK -^\™XÜÝ]\ËOœÞ[˜Ý[YKÝÚ[™ÝÊÌJJ�Ú[™ÝÊÝÚ[™ÝËJ^\™XÜÝ]\ËOœÞ[˜Ý[YJÌJKLJNÈ ËÈ Lˆ™XØ]\ÙH LH\ÈÈ ÈÚ^™KLBˆBˆB‚ˆ ËÈ›ÝÈ™XÈBˆYŠ^\™XÜÝ]\ËO˜�Y™™\ˆ ‰ˆ^\™XÜÝ]\ËO˜™YÚ[ŠBˆˆ›Üˆ -H ÈPVÔVQT”ÎÈ -ÊÈ -Bˆˆ™XÚÙ^KšÙ^\ÖÜHH^Y\–ÜKšÙ^\Έ™XÚÙ^K›™]ÚÙ^\ÖÜHH^Y\–ÜK›™]ÚÙ^\Έ™XÚÙ^Kœ™[X\ÙZÙ^\ÖÜHH^Y\–ÜKœ™[X\ÙZÙ^\Έ™XÚÙ^Kœ^ZÙ^\ÖÜHH^Y\–ÜKœ^ZÙ^\ΈBˆ™XÚÙ^K�[YHHÝ[YNˆ™XÚÙ^Kš[�\�˜[H[�\�˜[ˆ™XÚÙ^KœÞ[˜Ý[YHH^\™XÜÝ]\ËOœÞ[˜Ý[YNˆ ËÜ™XÚÙ^KœÙYYHÙ]ÙYY - -NˆY[XÜJ œ^\™XÜÝ]\ËO˜�Y™™\–Ü^\™XÜÝ]\ËOœÞ[˜Ý[YWK œ™XÚÙ^KÚ^™[ÙŠ™XÚÙ^JH -NˆB‚ˆYˆ -Ý[YH�HX^Ü™X×Ý[YH -HÝÜ™XÛÜ™[œ]Ê -NÈ ËÈØY™BˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×Ô‘PÊH -ÊÜ^\™XÜÝ]\ËOœÞ[˜Ý[YN‚ˆ ËÙX�Y×Üš[�Š�[YNˆ YÞ[˜Îˆ Y‹ -LÌŠ][YK -LÌŠ\^\™XÜÝ]\ËOœÞ[˜Ý[YJNˆ ËÙX�Y×Üš[�ŠšÙ^\Έ Y‹^Y\–ÌKœ™[X\ÙZÙ^\É‘“Q×ÐUPÒÊN‚ˆ™]\›ˆ NŸB‚š[�^T™XÛÜ™Y[œ]Ê -BžÂˆ[�H ˆ™XÒÙ^\È™XÚÙ^NˆÚ\ˆ]ÓPVÐT‘×ÓSˆ -È WNˆÚ^™WÝš[\Ú^™HH ˆÚ\ˆXY\–Í—N‚ˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×ÔVJH™]\›ˆ ˆYˆ -\^\™XÜÝ]\ËO˜™YÚ[ˆ -H ËÝ[YH�H^\™XÜÝ]\ËOœÝ\�[YH ‰‚ˆˆYˆ -Ý›[Š^\™XÜÝ]\ËOœ] -HH  -HÙ]˜\ÙT] -] ”Ø]™\È‹ -Nˆ[ÙHݘÜJ] ^\™XÜÝ]\ËOœ] -NˆYˆ -]ÜÝ›[Š] -KLWHOH ËÉÈ -HݘÜJ] ‹ÈŠN‚ˆYˆ -^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆœ™YJ^\™XÜÝ]\ËO˜�Y™™\ŠNˆ^\™XÜÝ]\ËO˜�Y™™\ˆH•SˆBˆYŠ^\™XÜÝ]\ËOš[™JBˆˆ˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆB‚ˆ^\™XÜÝ]\ËOš[™HH›Ü[ŠÝ˜Ø] -] ^\™XÜÝ]\ËO™š[[˜[YJKœ˜ŠÈŠNˆYŠ^\™XÜÝ]\ËOš[™JBˆˆœÙYZÊ^\™XÜÝ]\ËOš[™K  ÑQR×ÑS‘ -Nˆš[\Ú^™HH�[ -^\™XÜÝ]\ËOš[™JNˆœÙYZÊ^\™XÜÝ]\ËOš[™K  ÑQR×ÔÑU -NÈ ËÈ܈™]Ú[™ -^\™XÜÝ]\ËOš[™JB‚ˆYˆ -š[\Ú^™HH -Bˆˆš[�Š‘[\H™XÛÜ™Y[œ]Èš[K—ˆŠNˆ™]\›ˆ ˆBˆ^\™XÜÝ]\ËO˜�Y™™\ˆH -™XÒÙ^\ÊŠXØ[ØÊ Kš[\Ú^™JÌJNˆYŠ\^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆš[�Š‘\œ›ÜˆÈ[ØØ]H�Y™™\ˆ›Üˆ™XÛÜ™Y[œ]Ë—ˆŠNˆ™]\›ˆ ˆBˆH[ÙBˆˆš[�Š‘\œ›ÜˆÈÜ[ˆ™XÛÜ™Y[œ]Èš[K—ˆŠNˆ™]\›ˆ ˆBˆœ™XY - šXY\‹ ‹ K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËOœÝ\�[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËO™[™[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËO�ÝÞ[˜Ý[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËO˜ÜÙYY Ú^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËOœÙYY Ú^™[ÙŠZ[� �Ý -K K^\™XÜÝ]\ËOš[™JNˆœ™XY - œ^\™XÜÝ]\ËO�XÚÜËÚ^™[ÙŠ[œÚYÛ™Y -K K^\™XÜÝ]\ËOš[™JNˆœ™XY -^\™XÜÝ]\ËO˜�Y™™\‹Ú^™[ÙŠ™XÒÙ^\ÊJŠ^\™XÜÝ]\ËO™[™[YJÌJK K^\™XÜÝ]\ËOš[™JN‚ˆ ËÈÞ[˜È]Ý\�[YBˆÝ[YHH^\™XÜÝ]\ËOœÝ\�[YNˆ^\™XÜÝ]\ËOœÞ[˜Ý[YHH ˆ^\™XÜÝ]\ËO˜™YÚ[ˆH Nˆܘ[™ -^\™XÜÝ]\ËO˜ÜÙYY -Nˆܘ[™ ÌŠ^\™XÜÝ]\ËOœÙYY -Nˆ ËÜÙ]ÝXÚÜÊ[Y\—ÙÙ]XÚÊ -K\^\™XÜÝ]\ËO�XÚÜÊNˆB‚ˆ ËÈ›ÝÈ^HBˆYŠ^\™XÜÝ]\ËO˜�Y™™\ˆ ‰ˆ^\™XÜÝ]\ËO˜™YÚ[ˆ ‰ˆ^\™XÜÝ]\ËOœÞ[˜Ý[YH^\™XÜÝ]\ËO�ÝÞ[˜Ý[YJBˆˆY[XÜJ œ™XÚÙ^K œ^\™XÜÝ]\ËO˜�Y™™\–Ü^\™XÜÝ]\ËOœÞ[˜Ý[YWKÚ^™[ÙŠ™XÚÙ^JH -N‚ˆYŠÝ[YHOH™XÚÙ^K�[YH -BˆˆL̈™^Þ[˜Ý[YHH™XÚÙ^KœÞ[˜Ý[YN‚ˆ ËÝ[YHH -LÌŠ\™XÚÙ^K�[YNˆš[�Š”^H™XÛÜ™Y[œ]ΈÝ]ÙˆÞ[˜ÈH[YNˆ Y ™XÕ[YNˆ Yˆ‹[YK™XÚÙ^K�[YJNˆ ÊšYˆ -[�\�˜[OH™XÚÙ^Kš[�\�˜[ -Bˆˆ ËÚ[�\�˜[H -LÌŠ\™XÚÙ^Kš[�\�˜[ˆš[�Š”^H™XÛÜ™Y[œ]ΈÝ]ÙˆÞ[˜ÈH[�\�˜[ˆ Y ™XÒ[�\�˜[ˆ Yˆ‹[�\�˜[ ™XÚÙ^Kš[�\�˜[ -NˆJ‹Â‚ˆÚ[JÝ[YHˆ™XÚÙ^K�[YH ‰ˆ™^Þ[˜Ý[YHˆ  -HˆY[XÜJ œ™XÚÙ^K œ^\™XÜÝ]\ËO˜�Y™™\–ËK[™^Þ[˜Ý[YWKÚ^™[ÙŠ™XÚÙ^JH -NˆBˆÝ[YHH™XÚÙ^K�[YN‚ˆÚ[JÝ[YH™XÚÙ^K�[YH ‰ˆ™^Þ[˜Ý[YH^\™XÜÝ]\ËO�ÝÞ[˜Ý[YH -HˆY[XÜJ œ™XÚÙ^K œ^\™XÜÝ]\ËO˜�Y™™\–ÊÊÛ™^Þ[˜Ý[YWKÚ^™[ÙŠ™XÚÙ^JH -NˆBˆÝ[YHH™XÚÙ^K�[YNˆB‚ˆYˆ -Ý[YHOH™XÚÙ^K�[YH -BˆˆYˆ -^\™XÜÝ]\ËOœÞ[˜Ý[YHOH™XÚÙ^KœÞ[˜Ý[YH -BˆˆL̈™^Þ[˜Ý[YHH™XÚÙ^KœÞ[˜Ý[YN‚ˆš[�Š”^H™XÛÜ™Y[œ]ΈÝ]ÙˆÞ[˜ÈHÞ[˜Õ[YNˆ Y ™XÔÞ[˜Õ[YNˆ Yˆ‹^\™XÜÝ]\ËOœÞ[˜Ý[YK™XÚÙ^KœÞ[˜Ý[YJN‚ˆÚ[J^\™XÜÝ]\ËOœÞ[˜Ý[YHˆ™XÚÙ^KœÞ[˜Ý[YH ‰ˆ™^Þ[˜Ý[YHˆ  -HˆY[XÜJ œ™XÚÙ^K œ^\™XÜÝ]\ËO˜�Y™™\–ËK[™^Þ[˜Ý[YWKÚ^™[ÙŠ™XÚÙ^JH -NˆBˆ^\™XÜÝ]\ËOœÞ[˜Ý[YHH™XÚÙ^KœÞ[˜Ý[YN‚ˆÚ[J^\™XÜÝ]\ËOœÞ[˜Ý[YH™XÚÙ^KœÞ[˜Ý[YH ‰ˆ™^Þ[˜Ý[YH^\™XÜÝ]\ËO�ÝÞ[˜Ý[YH -HˆY[XÜJ œ™XÚÙ^K œ^\™XÜÝ]\ËO˜�Y™™\–ÊÊÛ™^Þ[˜Ý[YWKÚ^™[ÙŠ™XÚÙ^JH -NˆBˆ^\™XÜÝ]\ËOœÞ[˜Ý[YHH™XÚÙ^KœÞ[˜Ý[YNˆB‚ˆYˆ -[�\�˜[OH™XÚÙ^Kš[�\�˜[ -H[�\�˜[H™XÚÙ^Kš[�\�˜[ˆ ËÜܘ[™ ÌŠ™XÚÙ^KœÙYY -Nˆ›Üˆ -H ÈPVÔVQT”ÎÈ -ÊÈ -Bˆˆ^Y\–ÜKšÙ^\ÈH™XÚÙ^KšÙ^\ÖÜNˆ^Y\–ÜK›™]ÚÙ^\ÈH™XÚÙ^K›™]ÚÙ^\ÖÜNˆ^Y\–ÜKœ™[X\ÙZÙ^\ÈH™XÚÙ^Kœ™[X\ÙZÙ^\ÖÜNˆ^Y\–ÜKœ^ZÙ^\ÈH™XÚÙ^Kœ^ZÙ^\ÖÜNˆBˆ ËÚ[œ]™Yœ™\Ú -^\™XÜÝ]\ËOœÝ]\ÊNˆBˆB‚ˆ ËÙX�Y×Üš[�ŠœÞ[˜Ý[Nˆ YÝÞ[˜Îˆ YÝ]\ΉY‹^\™XÜÝ]\ËOœÞ[˜Ý[YK^\™XÜÝ]\ËO�ÝÞ[˜Ý[YK^\™XÜÝ]\ËOœÝ]\ÊNˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×ÔVJH -ÊÜ^\™XÜÝ]\ËOœÞ[˜Ý[YNˆYˆ -^\™XÜÝ]\ËOœÞ[˜Ý[YH�H^\™XÜÝ]\ËO�ÝÞ[˜Ý[YHÝ[YH�H^\™XÜÝ]\ËO™[™[YH -HÝÜ™XÛÜ™[œ]Ê -N‚ˆ ËÙX�Y×Üš[�Š�[YNˆ YÞ[˜Îˆ Y‹ -LÌŠ][YK -LÌŠ\^\™XÜÝ]\ËOœÞ[˜Ý[YJNˆ ËÙX�Y×Üš[�ŠšÙ^\Έ Y‹^Y\–ÌKœ™[X\ÙZÙ^\É‘“Q×ÐUPÒÊN‚ˆ™]\›ˆ NŸB‚š[�ÝÜ™XÛÜ™[œ]Ê -BžÂˆYŠ^\™XÜÝ]\ÊBˆˆÝÚ]Ú -^\™XÜÝ]\ËOœÝ]\ÊBˆˆØ\ÙHWÔ‘P×Ô‘P΂ˆˆÚ\ˆ]ÓPVÐT‘×ÓSˆ -È WNˆÚ\ˆXY\–Í—HH’S” LŽÂ‚ˆYˆ -Ý›[Š^\™XÜÝ]\ËOœ] -HH  -HÙ]˜\ÙT] -] ”Ø]™\È‹ -Nˆ[ÙHݘÜJ] ^\™XÜÝ]\ËOœ] -NˆYˆ -]ÜÝ›[Š] -KLWHOH ËÉÈ -HݘÜJ] ‹ÈŠN‚ˆYˆ -^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆ^\™XÜÝ]\ËOš[™HH›Ü[ŠÝ˜Ø] -] ^\™XÜÝ]\ËO™š[[˜[YJK�ØŠÈŠNˆYŠ^\™XÜÝ]\ËOš[™JBˆˆ^\™XÜÝ]\ËO™[™[YHHÝ[YNˆYˆ -^\™XÜÝ]\ËOœÞ[˜Ý[YH ŠH^\™XÜÝ]\ËOœÞ[˜Ý[YHH ŽÂˆ[ÙH^\™XÜÝ]\ËOœÞ[˜Ý[YH OH ŽÂ‚ˆ�Üš]JXY\‹ ‹ K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËOœÝ\�[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËO™[™[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËOœÞ[˜Ý[YKÚ^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËO˜ÜÙYY Ú^™[ÙŠLÌŠK K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËOœÙYY Ú^™[ÙŠZ[� �Ý -K K^\™XÜÝ]\ËOš[™JNˆ�Üš]J œ^\™XÜÝ]\ËO�XÚÜËÚ^™[ÙŠ[œÚYÛ™Y -K K^\™XÜÝ]\ËOš[™JNˆ�Üš]J^\™XÜÝ]\ËO˜�Y™™\‹Ú^™[ÙŠ™XÒÙ^\ÊJŠ^\™XÜÝ]\ËOœÞ[˜Ý[YJÌJK K^\™XÜÝ]\ËOš[™JNˆ™›\Ú -^\™XÜÝ]\ËOš[™JNÈ ËÈØY™Bˆ˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆH[ÙH™]\›ˆ ‚ˆœ™YJ^\™XÜÝ]\ËO˜�Y™™\ŠNˆ^\™XÜÝ]\ËO˜�Y™™\ˆH•SˆH[ÙH™]\›ˆ ˆœ™XZΈBˆØ\ÙHWÔ‘P×ÔVN‚ˆˆYŠ^\™XÜÝ]\ËOš[™JBˆˆYˆ -^\™XÜÝ]\ËOš[™JH˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆ[ÙH™]\›ˆ ˆBˆœ™XZΈBˆØ\ÙHWÔ‘P×ÔÕÔ‚ˆˆYŠ^\™XÜÝ]\ËOš[™JBˆˆYˆ -^\™XÜÝ]\ËOš[™JH˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆ[ÙH™]\›ˆ ˆBˆœ™XZΈBˆB‚ˆ^\™XÜÝ]\ËOœÝ]\ÈHWÔ‘P×ÔÕÔˆ^\™XÜÝ]\ËO˜™YÚ[ˆH ˆ^\™XÜÝ]\ËOœÞ[˜Ý[YHH ˆœ™YT™XÛÜ™Y[œ]Ê -NˆH™]\›ˆ ‚ˆ™]\›ˆ NŸB‚š[�œ™YT™XÛÜ™Y[œ]Ê -BžÂˆ^\™XÜÝ]\ËOœÝ]\ÈHWÔ‘P×ÔÕÔˆYŠ^\™XÜÝ]\ËOš[™JH˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆYŠ^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆœ™YJ^\™XÜÝ]\ËO˜�Y™™\ŠNˆ^\™XÜÝ]\ËO˜�Y™™\ˆH•Sˆ™]\›ˆ NˆB‚ˆ™]\›ˆ ŸB‚˜WÜ^\™XÜÝ]\ʈ[š]Ú[œ]Ü™XÛÜ™\Š -BžÂˆ^\™XÜÝ]\ÈH -WÜ^\™XÜÝ]\ÊŠXØ[ØÊ KÚ^™[ÙŠ -œ^\™XÜÝ]\ÊJN‚ˆ™]\›ˆ^\™XÜÝ]\ΟB‚�›ÚYœ™YWÚ[œ]Ü™XÛÜ™\Š -BžÂˆYŠ^\™XÜÝ]\ÊBˆˆYŠ^\™XÜÝ]\ËO˜�Y™™\ŠBˆˆYŠ^\™XÜÝ]\ËOš[™JH˜ÛÜÙJ^\™XÜÝ]\ËOš[™JNˆœ™YJ^\™XÜÝ]\ËO˜�Y™™\ŠNˆ^\™XÜÝ]\ËO˜�Y™™\ˆH•SˆBˆœ™YJ^\™XÜÝ]\ÊNˆ^\™XÜÝ]\ÈH•SˆBŸB‚�›ÚY\]J[�[™Ø[YK[�\Ù]�ØZ] -BžÂˆ[�HH ˆ[�ÚÙ^\ÈH ‚ˆÚYˆÑˆYˆ -Ø]™Y]K™œÛ[Z]OH JH ËÈœÞ[˜È[˜X›Yˆˆ ËÈÈ™YXÙH[œ]][˜ÞKØZ][�[H\Ý \È - \ÊHÙˆHÝ\œ™[�ˆ ËÈœ˜[YHÈ™XY[œ]È܈È[ž][™È[ÙKˆÙHØ[ˆÙ]]Ø^HÚ]\È™XØ]\ÙBˆ ËÈHÔ\ÈÙˆ[[Ù\›ˆÛÛ\]\œÈ H]™[ˆÛ™\È[™ÝËY[™ Ý]]YÜÈ Bˆ ËÈ\™HÛÛ\]HÝ™\šÚ[›ÜˆÜ[�“Ô‰ÜÈ™YYË‚ˆÍ�\™Ù]Ý[YHH[Y\—Ý]XÚÜÊ -H -È L ÝšY[רÝ\œ™[�Ü™Yœ™\Úܘ]J -H H ˆM�Ý\œ™[�Ý[YHH[Y\—Ý]XÚÜÊ -NˆÚ[H -Ý\œ™[�Ý[YH\™Ù]Ý[YJBˆˆ\ÛY\ -\™Ù]Ý[YH HÝ\œ™[�Ý[YJNˆÝ\œ™[�Ý[YHH[Y\—Ý]XÚÜÊ -NˆBˆBˆÙ[™Y‚‚ˆÙ][�\�˜[ - -NˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×ÔVH ‰ˆWÜ]\ÙH ‰ˆ]™[ -HYˆ -\^T™XÛÜ™Y[œ]Ê -H -HÝÜ™XÛÜ™[œ]Ê -Nˆ[œ]™Yœ™\Ú -^\™XÜÝ]\ËOœÝ]\ÊNˆÚY™YˆÑP“Bˆ[ÝšYWÜ^X˜XÚ×Ý\]Jˆ -›Ý™]ÚÙ^\È ˆ -“Q×ÑTÐÈ“Q×ÐS–P•UÓŠJHOH ˆ -NˆÙ[™Y‚ˆYŠ^\™XÜÝ]\ËOœÝ]\ÈOHWÔ‘P×Ô‘PÈ ‰ˆWÜ]\ÙH ‰ˆ]™[ -HYˆ -\™XÛÜ™[œ]Ê -H -HÝÜ™XÛÜ™[œ]Ê -N‚ˆYˆ - -WÜ]\ÙH ‰ˆ[™Ø[YHOH JH[Ø^\Ý\]JBˆˆ^XÝ]WÝ\]\ØÜš\Ê -NˆB‚ˆ™]Ý[YHH ˆYŠWÜ]\ÙJBˆˆYŠ[™Ø[YHOH HÚXÚ×Ú[—ÜØÜ™Y[Š -JBˆˆ^XÝ]WÚÙ^\ØÜš\Ê -NˆB‚ˆYŠ -]™[ØÛÛ\]Y ‰ˆ]™[ O˜›ÜÜ×ÜÛÝÈOH“ÔÔ×ÔÓÕ×ÓÓˆ ‰ˆ]ÜÜYY\ -HÛÝÛ[Ý[Û‹�ÙÙÛHˆÓÕ×ÓSÕSÓ—ÓÑ‘ŠBˆˆYŠÛÝÛ[Ý[Û‹™\˜][ÛˆOHÛÝÛ[Ý[Û‹˜ÛÝ[�\ŠBˆˆ™]Ý[YHHÝ[YH -È[�\�˜[ˆBˆBˆ[ÙBˆˆ™]Ý[YHHÝ[YH -È[�\�˜[ˆB‚ˆÛÝÛ[Ý[Û‹˜ÛÝ[�\ŠÊΈYŠÛÝÛ[Ý[Û‹˜ÛÝ[�\ˆOH -ÛÝÛ[Ý[Û‹™\˜][Ûˆ -È JJBˆˆÛÝÛ[Ý[Û‹˜ÛÝ[�\ˆH ˆYŠÛÝÛ[Ý[Û‹�ÙÙÛHˆÓÕ×ÓSÕSÓ—ÓÓŠBˆˆÛÝÛ[Ý[Û‹™\˜][ÛˆHÛÝÛ[Ý[Û‹�ÙÙÛNˆBˆBˆYŠ™]Ý[YHˆÝ[YH -È L -Bˆˆ™]Ý[YHHÝ[YH -È L ˆB‚ˆÚ[JÝ[YH™]Ý[YJBˆˆYŠ[™Ø[YHOH H[Ø^\Ý\]JBˆˆ^XÝ]WÝ\][ÙÚXÜØÜš\Ê -NˆB‚ˆYŠ[™Ø[YHOH JBˆˆ\]WÜØÜ›Û\Š -NˆYŠYœ™Y^™X[ -Bˆˆ[�[ÜØ[]™HH ‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -\^Y\–ÚWK™[� -H -ÊØ[ÜØ[]™NˆBˆ[ÜØ[]™HH -[ÜØ[]™H�HPVÔVQT”ÊHÈ Hˆ ‚ˆYŠ]™[ OœÙ][YHˆ  -]™[ O�\HOH ˆ ‰ˆ[ÜØ[]™JJBˆ ËÚYŠ]™[ OœÙ][YHˆ  -]™[ O�\HOH ˆ ‰ˆ\^Y\–ÌK™[� ‰ˆ\^Y\–ÌWK™[� ‰ˆ\^Y\–Ì—K™[� ‰ˆ\^Y\–Ì×K™[� -JBˆˆ[�[ÜÛ›Ú›Ú[ˆH [ÜÛ›ØÜ™Y]ÈH ‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -\^Y\–ÚWKš›Ú[š[™ÊH -ÊØ[ÜÛ›Ú›Ú[ŽÂˆBˆ[ÜÛ›Ú›Ú[ˆH -[ÜÛ›Ú›Ú[ˆ�HPVÔVQT”ÊHÈ Hˆ ‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -^Y\–ÚWK˜Ü™Y]È JH -ÊØ[ÜÛ›ØÜ™Y]ΈBˆ[ÜÛ›ØÜ™Y]ÈH -[ÜÛ›ØÜ™Y]È�HPVÔVQT”ÊHÈ Hˆ ‚ˆYŠ[Y[Y�ˆ -Bˆˆ K][Y[Y�ˆBˆ[ÙHYŠ -]™[ OœÙ][YHˆ  ‰ˆ[ÜÛ›Ú›Ú[ŠHˆ - - -[›ÜÚ\™H ‰ˆÜ™Y]È JH -›ÜÚ\™H ‰ˆ[ÜÛ›ØÜ™Y]ÊJBˆ ‰ˆ[ÜÛ›Ú›Ú[ˆ -Bˆ -Bˆˆ[YWÛÝ™\Š -NˆBˆBˆBˆ\]WÜØÜ›ÛYØ™Ê -NˆYŠ]™[ O�\HOH ŠBˆˆ\]\Ý]\Ê -NˆBˆBˆYŠ[™Ø[YHOH HÚXÚ×Ú[—ÜØÜ™Y[Š -JBˆˆ\]WÙ[�Ê -NˆB‚ˆYŠ[™Ø[YHOH H[Ø^\Ý\]JBˆˆ^XÝ]WÝ\]YÙÚXÜØÜš\Ê -NˆB‚ˆ -Ê×Ý[YNˆB‚ˆB‚ˆ ÊŠŠŠŠŠŠŠŠŠŠŠˆÙž]Y]YZ[™È -ŠŠŠŠŠŠŠŠŠŠŠ‹Â‚ˆÛX\œØÜ™Y[ŠœØÜ™Y[ŠN‚ˆYŠ[™Ø[YHOH H ‰ˆWÜ]\ÙJBˆˆ˜]×ÜØÜ›ÛYØ™Ê -NˆYŠ]™[ O�\HOH ŠBˆˆ™Y˜]ÜÝ]\Ê -NˆBˆYŠ]™[ O�\HOH ŠBˆˆ˜]ÜÝ]\Ê -NˆBˆ˜]×Ý^ØšœÊ -NˆB‚ˆYŠZ[™Ø[YJBˆˆYŠ˜XÚÙÜ›Ý[™ -BˆˆÜš]\WØYÜØÜ™Y[Š  RS—ÒS• ˜XÚÙÜ›Ý[™ •S  -NˆBˆB‚ˆ ËÈ[�]HÜš]\È]Y]YZ[™ÂˆYŠ[™Ø[YHOH HÚXÚ×Ú[—ÜØÜ™Y[Š -JBˆYŠWÜ]\ÙJBˆˆ\Ü^WÙ[�Ê -NˆB‚ˆ ÊŠŠŠŠŠŠŠŠŠŠŠˆ\]YØÜš\ -ŠŠŠŠŠŠŠŠŠŠŠ‹ÂˆYŠ[™Ø[YHOH H[Ø^\Ý\]JBˆˆ^XÝ]WÝ\]YØÜš\Ê -NˆB‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -^Y\–ÚWK™[� ‰ˆ -^Y\–ÚWK›™]ÚÙ^\È ˆ“Q×ÔÕT• -JBˆˆÚÙ^\ÈH Nˆœ™XZΈBˆB‚ˆ ËÈ Œ LKÌL ÌŒˆUˆ[Ý™H]\ÙHY[�HÙÚXÈ\™Bˆ ÊšYŠ[™Ø[YHOH H ‰ˆWÜ]\ÙH ‰ˆ[›Ü]\ÙH ‰‚ˆ - -^Y\–ÌK™[� ‰ˆ -^Y\–ÌK›™]ÚÙ^\È ˆ“Q×ÔÕT• -JHˆ -^Y\–ÌWK™[� ‰ˆ -^Y\–ÌWK›™]ÚÙ^\È ˆ“Q×ÔÕT• -JHˆ -^Y\–Ì—K™[� ‰ˆ -^Y\–Ì—K›™]ÚÙ^\È ˆ“Q×ÔÕT• -JHˆ -^Y\–Ì×K™[� ‰ˆ -^Y\–Ì×K›™]ÚÙ^\È ˆ“Q×ÔÕT• -JJBˆ -J‹ÂˆYŠ[™Ø[YHOH H ‰ˆWÜ]\ÙH ‰ˆ[›Ü]\ÙH ‰ˆÚÙ^\ÊBˆˆYˆ -JÛÝ×ÛXZ[›Y[�WÙ›YÉŒJH -BˆˆÛÝ[™Ü]\ÙWÛ]\ÚXÊ JNˆÛÝ[™Ü]\ÙWÜØ[\J JNˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý œ]\ÙK Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -Nˆ]\Ù[Y[�J -Nˆ™]\›ŽÂˆBˆBˆYŠ[™Ø[YHOH H ‰ˆ -ÛÝ×ÛXZ[›Y[�WÙ›YÉŒJH -Bˆˆ˜XÚÝ×ÛXZ[›Y[�J -Nˆ™]\›ŽÂˆB‚ˆ ÊŠŠŠŠŠŠŠŠŠˆ\]HØÜ™Y[ˆ -ŠŠŠŠŠŠŠŠŠŠŠŠŠ‹Â‚ˆÜš]\WÙ˜]ÊœØÜ™Y[‹ RS—ÒS• PVÒS•   -NÈ ËÈ›ÝXÙK[Ø^\Ș]ÈÜš]\È]H™\žH[™ÙˆÝ\ˆY]Ù‚ˆYŠÜ]\ÙHOH ˆ ‰ˆ[›ÜØÜ™Y[œÚÝ ‰ˆ -›Ý™]ÚÙ^\È ˆ“Q×ÔÐÔ‘QS”ÒÕ -JBˆˆØÜ™Y[œÚÝ -œØÜ™Y[‹Ù][  JNˆB‚ˆ ËÈX�YÈÝY™‹ÚÝ[›Ý\X\ˆÛˆØÜ™Y[œÚ݈YŠX�Y×Ý[YHOH ‘‘‘‘‘‘‘ŠBˆˆX�Y×Ý[YHHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ NˆBˆYŠÝ[YHX�Y×Ý[YH ‰ˆX�Y×Û\ÙÖÌJBˆˆYŠX�Y×ÞWÛ\ÙËž�H  ‰ˆX�Y×ÞWÛ\ÙËžH�H  -BˆˆYˆ -X�Y×ÞWÛ\ÙË™›Û�Ú[™^  -HX�Y×ÞWÛ\ÙË™›Û�Ú[™^H ˆØÜ™Y[—Üš[�ŠœØÜ™Y[‹X�Y×ÞWÛ\ÙËž X�Y×ÞWÛ\ÙËžKX�Y×ÞWÛ\ÙË™›Û�Ú[™^ X�Y×Û\ÙÊNˆH[ÙHØÜ™Y[—Üš[�ŠœØÜ™Y[‹ šY[Û[Ù\Ë�”™\ËY›Û�ZYÚ - -K X�Y×Û\ÙÊNˆBˆ[ÙBˆˆX�Y×Û\ÙÖÌHH ˆX�Y×ÞWÛ\ÙËžH LNˆX�Y×ÞWÛ\ÙËžHH LNˆÚY™YˆP•Q×ÓSÑBˆYŠ]™[ OœÜÊBˆˆX�Y×Üš[�Š”ÜÚ][ÛŽˆ ZKÚYˆ ZKÜ]ÛŽˆ ZKÙ™œÙ]Έ ZKÉZH‹]™[ OœÜË]™[ O�ÚY Ý\œ™[�ÜÜ]Û‹]™[ Oœ]XZÙKÙžÞWÛÙ™œÙ] -NˆBˆÙ[™Y‚ˆB‚ˆYŠ\Ù]�ØZ] -Bˆˆ™ØWÝ�ØZ] - -NˆBˆšY[רÛÜWÜØÜ™Y[ŠœØÜ™Y[ŠNˆÜš]\WØÛX\Š -N‚ˆÚXÚ×Û]\ÚXÊ -NˆÛÝ[™Ý\]WÛ]\ÚXÊ -NŸB‚‚‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‹ÊˆÛX›ÈKÍ ÌŒ Lˆ™]È�[˜Ý[Ûˆ]Ø[ˆ\ÙHœšYÚ™\ÜËÙØ[[XHÛÜœ™XÝ[Û‚ˆ -ˆ[™\[™[�œ›ÛHHÛؘ[[]HÛˆ]›Ü›\ÈÚ\™H] ÜÈ]˜Z[X›K‚ˆ -ˆ\™Ø\™HXØÙ[\˜]YœšYÚ™\ÜËÙØ[[XHÛÜœ™XÝ[Ûˆ\È]˜Z[X›HÛˆÜ[‘Óˆ -ˆ]›Ü›\È\Ú[™ÈÓÓ ˆ™]\›œÈ HÛˆÝXØÙ\ÜË Ûˆ\œ›Ü‹‚ˆ -‹Âš[�Ù]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠ[�ÛK[�œŠBžÂˆšY[×ÜÙ]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠÛKœŠNˆ™]\›ˆ NŸB‚‹ËÈÚ[\H[]H˜YH ÈœØÜ™Y[ˆ˜YB�›ÚY˜YWÛÝ] -[�\K[�ÜYY -BžÂˆ[�KˆH ˆ[�‹ÈH ˆL̈[�\�˜[H ˆ[�Ý\œ™[�HÜYYÈÜYYˆ˜YNˆ×ÜØÜ™Y[ˆ -™˜�Y™™\ˆH•Sˆ×Ù˜]ÛY]ÙHHZ[›Y]ÙˆK˜[HH“S‘ÓSÑWÐU‘T�QÑN‚ˆ›ÜŠHH ˆH Ȉ �È -BˆˆÚ[JˆHH ‰ˆˆ � -BˆˆYŠ]\H\HOH JBˆˆˆH - -Ø]™Y]K˜œšYÚ™\ÜÈ -È �MŠH -ˆ - � HŠH È � -H H �MŽÂˆÈH �Mˆ H - -Ø]™Y]K™Ø[[XH -È �MŠH -ˆ - � HŠH È � -Nˆ™ØWÝ�ØZ] - -NˆYŠ\Ù]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠËŠJBˆˆYŠY˜�Y™™\ŠBˆˆ˜�Y™™\ˆH[ØÜØÜ™Y[ŠœØÜ™Y[‹O�ÚY œØÜ™Y[‹OšZYÚ œØÜ™Y[‹Oœ^[›Ü›X] -NˆÛÜ\ØÜ™Y[Š˜�Y™™\‹œØÜ™Y[ŠNˆBˆ ËÌ�MH -È[H“S‘ÓSÑWÐU‘T�QÑH\ÈXÝX[H[ˆ›[™ ÛÈ\ÙH �M[œÝXYˆK˜Ú[›™[ˆHK˜Ú[›™[ÈHK˜Ú[›™[ˆH �M -ˆ - � HŠH È �ˆÛX\œØÜ™Y[ŠœØÜ™Y[ŠNˆ]ØÜ™Y[ŠœØÜ™Y[‹˜�Y™™\‹   ™JNˆBˆBˆŠÊΈYŠ]\H\HOH JBˆˆšY[רÛÜWÜØÜ™Y[ŠœØÜ™Y[ŠNˆBˆBˆYŠ]\H\HOH ŠBˆˆÛÝ[™Ý\]WÛ]\ÚXÊ -NˆYŠ[]\ÚXÛÝ™\›\ -BˆˆÛÝ[™Ý›Û[YWÛ]\ÚXÊØ]™Y]K›]\ÚXÝ›Û -ˆ - � HŠH È � Ø]™Y]K›]\ÚXÝ›Û -ˆ - � HŠH È � -NˆBˆBˆ[�\�˜[H[Y\—ÙÙ][�\�˜[ -Ý\œ™[� -NˆYŠ[�\�˜[ˆÝ\œ™[� -Bˆˆ[�\�˜[HÝ\œ™[� È ŒˆBˆYŠ[�\�˜[ˆÝ\œ™[� È -Bˆˆ[�\�˜[HÝ\œ™[� È ˆBˆH -ÏH[�\�˜[ˆB‚ˆYŠ]\H\HOH ŠBˆˆYŠ[]\ÚXÛÝ™\›\ -BˆˆÛÝ[™ØÛÜÙWÛ]\ÚXÊ -NˆBˆB‚ˆYŠ]\H\HOH JBˆˆÛX\œØÜ™Y[ŠœØÜ™Y[ŠNˆšY[רÛÜWÜØÜ™Y[ŠœØÜ™Y[ŠNˆ™ØWÝ�ØZ] - -Nˆ ËÝH›XÚÈØÜ™Y[‹ÛÈÙH™]\›ˆÈ›Ü›X[[]BˆÙ]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠØ]™Y]K™Ø[[XKØ]™Y]K˜œšYÚ™\ÜÊNˆB‚ˆYŠ˜�Y™™\ŠBˆˆœ™Y\ØÜ™Y[Š ™˜�Y™™\ŠNˆBŸB‚‚‚�›ÚY\WØÛÛ�›ÛÊ -BžÂˆ[�‚ˆ›ÜŠH ÈPVÔVQT”ÎÈ -ÊÊBˆˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÑTÐËÓÓ•“ÓÑTÐÊNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÓSÕ‘UT Ø]™Y]KšÙ^\ÖÜVÔÑQÓSÕ‘UTJNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÓSÕ‘QÕÓ‹Ø]™Y]KšÙ^\ÖÜVÔÑQÓSÕ‘QÕÓ—JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÓSÕ‘SQ• Ø]™Y]KšÙ^\ÖÜVÔÑQÓSÕ‘SQ•JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÓSÕ‘T’QÒ Ø]™Y]KšÙ^\ÖÜVÔÑQÓSÕ‘T’QÒJNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÐUPÒËØ]™Y]KšÙ^\ÖÜVÔÑQÐUPÒ×JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÐUPÒÌ‹Ø]™Y]KšÙ^\ÖÜVÔÑQÐUPÒÌ—JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÐUPÒÌËØ]™Y]KšÙ^\ÖÜVÔÑQÐUPÒÌ×JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÐUPÒÍ Ø]™Y]KšÙ^\ÖÜVÔÑQÐUPÒÍJNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×Ò•ST Ø]™Y]KšÙ^\ÖÜVÔÑQÒ•STJNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÔÔPÒPS Ø]™Y]KšÙ^\ÖÜVÔÑQÔÔPÒPSJNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÔÕT• Ø]™Y]KšÙ^\ÖÜVÔÑQÔÕT•JNˆÛÛ�›ÛÜÙ]Ù^J^Y\˜ÛÛ�›ÛÚ[�\œÖÜK“Q×ÔÐÔ‘QS”ÒÕ Ø]™Y]KšÙ^\ÖÜVÔÑQÔÐÔ‘QS”ÒÕJNˆBŸB‚‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚�›ÚY\Ü^WØÜ™Y]Ê -BžÂˆL̈š[š\Ú[YHHÝ[YH -È L -ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYYˆ[�Û™HH ˆ[�ÈHšY[Û[Ù\Ë�”ÚY� È ˆ -È Îˆ[�ˆH -šY[Û[Ù\Ë�”™\È HšY[Û[Ù\Ë�”ÚY� -H È �ˆ[�HH ˆ[�HšY[Û[Ù\Ëš™\È È ŽÂˆ[�ÛÛ HH H›Û�[Û›ÝÚY - -H -ˆ MŽÂˆ[�ÛÛ ˆH -È›Û�[Û›ÝÚY - -H -ˆ ‚ˆYŠØ]™Y]K›ÙÛÈOH JBˆˆ™]\›ŽÂˆBˆ˜YWÛÝ] -  -N‚ˆ[›ØYÛ]™[ - -N‚ˆ›Ý™]ÚÙ^\ÈH ‚ˆÚ[JYÛ™JBˆˆHH ŽÂ‚ˆ›Û�Üš[�ŠÜÝ›ZY - ‹�Ü™Y]ÈŠKË ‹ �Ü™Y]ÈŠN‚ˆ›Û�Üš[�ŠÜÝ›ZY - K“Ü[�“ÔˆŠKÈ -Ȉ -ˆK K “Ü[�“ÔˆŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  �Ø\ÚÙ^K[[Ûˆ‹ˆŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ”›Ú™XÝXYŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  “\ÛX[ZÍŽ HŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ‘]™[Ü\ˆŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  ’ܘ]\ÈŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ‘]™[Ü\ˆŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÜÝ›ZY - K‘›Ü›Y\ˆÝY™ˆŠKÈ -Ȉ -ˆK K ‘›Ü›Y\ˆÝY™ˆŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  ‘šYÚˆÛܙȊNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ‘�YÝYHŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  ’Ð�[™™\ÜÙ[ˆŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ’Ú\˜žHŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  “Ü™˜[ŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  “Ü›ØÚWÖŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  ”ÖŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  •Z[ÈŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  �U[›™[ÈŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  •Ú]H˜YÛÛˆŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  ”ÛX›ÈŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÜÝ›ZY - K”Ü�ÈŠKÈ -Ȉ -ˆK K ”Ü�ÈŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  “[�^ ÓÔÖŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  ”ÖŠNÈ -ÊÛN‚ˆ›Û�Üš[�ŠÛÛ KÈ -Ȉ -ˆK  �[™›ÚYŠNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  �Ôž‘YKÛX›ËŠNÈ -ÊÛNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  �U[›™[Ë\ÛX[ZÍŽ HŠNÈ -ÊÛNˆ›Û�Üš[�ŠÛÛ ‹È -Ȉ -ˆK  •Ú]H˜YÛÛˆŠNÈ -ÊÛN‚ˆ\]J ‹ -N‚ˆÛ™HH -Ý[YHˆš[š\Ú[YJNˆÛ™HH -›Ý™]ÚÙ^\È ˆ -“Q×ÔÕT• -È“Q×ÑTÐÊJNˆBˆ˜YHH ÍNˆ˜YWÛÝ] -  -NŸB‚‚�›ÚY›Ü”Ú]ÝÛŠ[�Ý]\ËÛÛœÝÚ\ˆ -›\ÙË ‹‹ŠBžÂˆ˜WÛ\Ý\™Û\݈˜WÛ\ÝÝ]]Ø\™Ý[Y[�Έ[�N‚ˆÝ]XÈ[�Ú][™ÙÝÛˆH ‚ˆYŠÚ][™ÙÝÛŠBˆˆ™]\›ŽÂˆB‚ˆÚ][™ÙÝÛˆH Nˆ˜WÜÝ\� -\™Û\Ý \ÙÊN‚ˆ ËÜš[�ŠœØ]™Y]K›ÙÛÈ Yˆ‹Ø]™Y]K›ÙÛÊN‚ˆYŠY\ØX›[ÙÊBˆˆÝÚ]Ú -Ý]\ÊBˆˆØ\ÙH ‚ˆš[�Š—ŠŠŠŠŠŠŠŠŠŠŠŠˆÚ][™ÈÝÛˆ -ŠŠŠŠŠŠŠŠŠŠŠ——ˆŠNˆœ™XZΈY˜][‚ˆš[�Š—ŠŠŠŠŠŠŠŠŠŠˆ[ˆ\œ›ÜˆØØÝ\œ™Y -ŠŠŠŠŠŠŠŠŠˆ‚ˆ—ŠˆÚ][™ÈÝÛˆ -——ˆŠNˆœ™XZΈBˆB‚ˆYŠY\ØX›[ÙÊBˆˆ˜WØÛÜJÝ]]Ø\™Ý[Y[�Ë\™Û\Ý -NˆÜš]UÓÙÑš[UŠ\ÙËÝ]]Ø\™Ý[Y[�ÊNˆ˜WÙ[™ -Ý]]Ø\™Ý[Y[�ÊNˆB‚‚ˆÙ]˜[TÝ]\Ê–UTÊNˆØ]™\Ù][™ÜÊ -N‚ˆ ʈ[�žHÚ[�›ÜˆH[™Ú[™HÜ™Y]ÈØÜ™Y[‹ˆ -‹Âˆ ËÜØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÑS‘ÒS‘WÐÔ‘QUÂBB‚ˆ ËÈYŠÝ]\ÈOH ŠBˆ ËȈ ËÈ\Ü^WØÜ™Y]Ê -Nˆ ËÈB‚ˆYŠÝ\�\ÙÛ™JBˆÈˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÑS‘ÒS‘WÐÔ‘QUˆ\›WÝšY[Û[Ù\Ê -NˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙH]™[]HŠNˆBˆYˆ -Ý\�\ÙÛ™JBˆˆ[›ØYÛ]™[Ü™\Š -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹‹‹‹‹‹‹‹‹‹—ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆ[›ØYÛ]™[ - -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‘Û™HW—ˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙHܘ\XÜÈ]HŠNˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆœ™Y\ØÜ™Y[Š �œØÜ™Y[ŠNÈ ËÈ[ØØ]YžH[š]ÝšY[Û[Ù\ˆBˆYŠÝ\�\ÙÛ™H ‰ˆ^[›Ü›X]OHVSÞ -H›ÜŠHH ÈHPVГS‘S‘ÔÎÈJÊÊBˆˆœ™YJ›[™X›\ÖÚWJNˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆœ™Y\ØÜ™Y[Š ˜˜XÚÙÜ›Ý[™ -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹ˆŠNˆBˆÚY™YˆÐPÒWÐ�PÒÑÔ“ÕS‘ˆYŠÝ\�\ÙÛ™JH›ÜŠHH ÈHPVÐÐPÒQÐ�PÒÑÔ“ÕS‘ÎÈJÊÊBˆˆœ™Y\ØÜ™Y[Š ˜™×ØØXÚVÚWJNˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹ˆŠNˆBˆÙ[™Y‚ˆYŠÝ\�\ÙÛ™JBˆˆœ™Y\Üš]\Ê -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆ[›ØYØ[Ù›Û�Ê -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—Û™HWˆŠNˆB‚‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙHØ[YH]K‹‹‹‹‹‹‹‹‹‹‹——ˆŠNˆB‚ˆYŠÝ\�\ÙÛ™JBˆˆœ™YWÙ[�Ê -NˆBˆYŠÝ\�\ÙÛ™JBˆˆœ™YWÛ[Ù[Ê -NˆBˆYŠÝ\�\ÙÛ™JBˆˆœ™YWÛ[Ù[ØXÚJ -NˆBˆYŠÝ\�\ÙÛ™JBˆˆÛX\—ÜØÜš\Ê -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—”™[X\ÙHØ[YH]K‹‹‹‹‹‹‹‹‹‹‹—Û™HWˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙH[Y\‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆ›Ü•[Y\‘^] - -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—Û™HWˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙH[œ]\™Ø\™K‹‹‹‹‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆÛÛ�›ÛÙ^] - -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—Û™HWˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙHÛÝ[™Þ\Ý[K‹‹‹‹‹‹‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆÛÝ[™Ù^] - -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—Û™HWˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š”™[X\ÙHš[PØXÚ[™ÈÞ\Ý[K‹‹ˆŠNˆBˆYŠÝ\�\ÙÛ™JBˆˆZ×Ý\›J -NˆBˆYŠY\ØX›[ÙÊBˆˆš[�Š—Û™HWˆŠNˆB‚ˆYŠ[Ù[ÛY\Ý -Bˆˆœ™YPÛÛ[X[™\Ý -[Ù[ÛY\Ý -NÈ ËÈ[Ý™Y\™H™XØ]\ÙH\Ý\È›Ý[š]X[^™YYˆÚ]ÝÛˆ\È[š]X]Yœ›ÛH[œÚYHHY[�BˆBˆYŠ[Ù[ÝÛY\Ý -Bˆˆœ™YPÛÛ[X[™\Ý -[Ù[ÝÛY\Ý -NˆBˆYŠ]™[ÛY\Ý -Bˆˆœ™YPÛÛ[X[™\Ý -]™[ÛY\Ý -NˆBˆYŠ]™[Ü™\˜ÛY\Ý -Bˆˆœ™YPÛÛ[X[™\Ý -]™[Ü™\˜ÛY\Ý -NˆB‚ˆœ™YS[Ù[\Ý - -NˆÛX\—ÜØ]™YØ[ÝÜÙ[XÝØ\™Ý[Y[�Ê -Nˆœ™YJØ]™[]™[Ø[ÝÜÙ[XÝØ\™ÜÊNˆØ]™[]™[Ø[ÝÜÙ[XÝØ\™ÜÈH•SˆYŠØ]™[]™[ -Bˆˆœ™YJØ]™[]™[ -NˆØ]™[]™[H•SˆBˆØ]™[]™[ØÛÝ[�H ˆœ™YJ[ÝÜÙ[XÝØ\™ÜÊNˆ[ÝÜÙ[XÝØ\™ÜÈH•Sˆœ™YYš[[˜[YXØXÚJ -NˆØ—Ý\›]˜[œÊ -N‚ˆ ËÈœ™YH[œ]™XÛÜ™\‚ˆœ™YWÚ[œ]Ü™XÛÜ™\Š -N‚ˆYŠY\ØX›[ÙÊBˆˆš[�Š—ŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠˆÛ™H -ŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠ——ˆŠNˆB‚ˆYŠY\ØX›[ÙÊBˆˆ˜WØÛÜJÝ]]Ø\™Ý[Y[�Ë\™Û\Ý -NˆÜš]UÓÙÑš[UŠ\ÙËÝ]]Ø\™Ý[Y[�ÊNˆ˜WÙ[™ -Ý]]Ø\™Ý[Y[�ÊNˆB‚ˆ˜WÙ[™ -\™Û\Ý -NˆÚ][™ÙÝÛˆH ˆ›Ü‘^] -Ý]\ÊNŸB‚‚�›ÚYÝZ\Ý\�\ - -BžÂˆ[�N‚ˆYŠY›Û�ÛØY - ›Y[�KÙ›Û� H‹XÚÙš[K -JBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈØY›Û�ÌHWˆŠNˆBˆYŠY›Û�ÛØY - K›Y[�KÙ›Û� ˆ‹XÚÙš[K -JBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈØY›Û�̈WˆŠNˆBˆYŠY›Û�ÛØY - ‹›Y[�KÙ›Û� È‹XÚÙš[K -JBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈØY›Û�ÌÈWˆŠNˆB‚‚ˆ›Ü•[Y\’[š] - -N‚ˆÛÛ�›ÛÚ[š] - ŠNˆ\WØÛÛ�›ÛÊ -N‚ˆ[š]ÝšY[Û[Ù\Ê -NˆYŠ]šY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊJBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈÙ]šY[È[ÙNˆ Y YWˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ÊNˆB‚ˆ›ÜŠHH ÈH �MŽÈJÊÊBˆˆ™[Û�X›VÚWHHNˆBŸB‚‚�›ÚYÝ\�\ - -BžÂˆ[�N‚ˆš[�Š‘š[PØXÚ[™ÈÞ\Ý[H[š] ‹‹‹‹‹‹—ŠNˆYŠZ×Ú[š] - -JBˆˆš[�Š‘[˜X›YˆŠNˆBˆ[ÙBˆˆš[�Š‘\ØX›YˆŠNˆB‚ˆØYYÚØÛÜ™Qš[J -NˆÛX\”Ø]™YØ[YJ -N‚ˆ[š]ÝšY[Û[Ù\Ê JNˆYŠ]šY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊJBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈÙ]šY[È[ÙNˆ Y YWˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ÊNˆB‚ˆš[�Š•[Y\ˆ[š] ‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆ›Ü•[Y\’[š] - -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š’[š]X[^™HÛÝ[™ ‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆYŠÛÝ[™Ú[š] - -JBˆˆYŠØYÜÜXÚX[ÜÛÝ[™Ê -JBˆˆš[�Š‘Û™HWˆŠNˆBˆ[ÙBˆˆš[�Š—ˆŠNˆBˆYŠ\ÛÝ[™ÜÝ\�Ü^X˜XÚÊ -JBˆˆš[�Š•Ø\›š[™ÎˆØ[‰Ý^HÛÝ[™WˆŠNˆBˆЗÜÙ]›Û[YJЗÓPTÕT•“Ó Ø]™Y]KœÛÝ[™›Û -NˆЗÜÙ]›Û[YJЗՓÒPÑU“Ó Ø]™Y]KœÛÝ[™›Û -NˆBˆ[ÙBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈ[š]X[^™HÛÝ[™ —ˆŠNˆB‚ˆ ËÈ[š] ˆ[œ]™XÛÜ™\‚ˆ[š]Ú[œ]Ü™XÛÜ™\Š -N‚ˆš[�Š“ØY[™È›Û�Ë‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆØYØ[Ù›Û�Ê -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™ÈÜš]\Ë‹‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆØYÜÜXÚX[ÜÜš]\Ê -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™È]™[Ü™\‹‹‹‹‹‹‹‹‹‹—ŠNˆØYÛ]™[Ü™\Š -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™È[Ù[ÛÛœÝ[�Ë‹‹‹‹‹—ŠNˆØYÛ[Ù[ØÛÛœÝ[�Ê -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™ÈØÜš\Ù][™ÜË‹‹‹‹‹—ŠNˆØYÜØÜš\ÜÙ][™Ê -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™ÈØÜš\Ë‹‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆØYÜØÜš\Ê -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™È[Ù[Ë‹‹‹‹‹‹‹‹‹‹‹‹‹‹——ˆŠNˆØYÛ[Ù[Ê -N‚ˆš[�Š“Øš™XÝ[™Ú[™H[š] ‹‹‹‹‹‹‹‹‹‹—ŠNˆYŠX[Ø×Ù[�Ê -JBˆˆ›Ü”Ú]ÝÛŠ K“›Ý[›ÝYÚY[[ÜžH›ÜˆØ[YHØš™XÝÈWˆŠNˆBˆš[�Š‘Û™HWˆŠN‚ˆš[�Š“ØY[™ÈY[�K� ‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆØYÛY[�WÝ - -Nˆš[�Š‘Û™HWˆŠN‚ˆ Ê‚ˆܘ]\È - KLŒ � -H[Ý™YH˜[œÛ][Ûˆ[™Y[�H�[˜Ý[ÛœÈÈH[™ÙˆH[™Ú[™HœÝ\�\ˆ�[˜Ý[Û‹ˆ�]™Y›Ü™HH˜ÛÛ�›Û[š]ˆ�[˜Ý[Ûˆ -™]™\�YH›Û��[˜Ý[ÛˆÈØY™Y›Ü™HØÜš\ÊBˆ -‹Âˆš[�Š“ØY[™È˜[œÛ][Û‹‹‹‹‹‹‹‹‹‹—ŠNˆØ—Ú[š]˜[œÊ -Nˆš[�Š‘Û™HWˆŠN‚ˆš[�Š’[œ][š] ‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹—ŠNˆÛÛ�›ÛÚ[š] -Ø]™Y]K�\ÙZ›ÞJNˆ\WØÛÛ�›ÛÊ -Nˆš[�Š‘Û™HWˆŠN‚ˆÚY™YˆÐPÒWÐ�PÒÑÔ“ÕS‘ˆš[�Š�ØXÚ[™È˜XÚÙÜ›Ý[™Ë‹‹‹‹‹‹‹‹‹—ŠNˆØXÚWØ[ؘXÚÙÜ›Ý[™Ê -Nˆš[�Š‘Û™HWˆŠNˆÙ[™Y‚‚ˆš[�Š�Ü™X]H›[™[™ÈX›\Ë‹‹‹‹‹‹—ŠNˆYŠ^[›Ü›X]OHVSÞ -BˆˆÜ™X]WØ›[™ÝX›\×Þ -›[™X›\ÊNˆB‚ˆ›ÜŠHH ÈHPVÔSÔÒV‘H È ÈJÊÊBˆˆ™[Û�X›VÚWHHNˆBˆš[�Š‘Û™HWˆŠN‚ˆYŠØ]™Y]K›ÙÛÊÊȈ L -BˆˆØ]™Y]K›ÙÛÈH ˆB‚ˆš[�Š”Ø]™HÙ][™ÜÈÛȘ\‹‹‹‹‹‹‹‹‹—ŠNˆØ]™\Ù][™ÜÊ -Nˆš[�Š‘Û™HWˆŠN‚ˆÝ\�\ÙÛ™HH N‚ˆš[�Š——ˆŠN‚ŸB‚‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚‚‹ËÈ™]\›œÈ Ûˆ\œ›Ü‹ LHÛˆ\ØØ\Bš[�^YÚYŠÚ\ˆ -™š[[˜[YK[� [�K[�›ÜÚÚ\ -BžÂˆ[šYÚY—Ú[™›È -š[™›ÈHØ[ØÊ KÚ^™[ÙŠ -š[™›ÊJNˆ×ÜØÜ™Y[ˆ -˜˜XÚØ�Y™™\ˆH•Sˆ×ÜØÜ™Y[ˆ -�[\™ÈH˜XÚÙÜ›Ý[™ˆ[�™\Ý[H NˆL̈Þ[˜ÝÜÛÝ[™ˆL̈\Ý[YNˆL̈Z[\ÙXÛÛ™ΈL̈[\[YK[\™]Ý[YNÈ ËÈ[\ܘ\žH]ڛ܈[™Ø[YHÚYˆ^B‚ˆÞ[˜ÝÜÛÝ[™H -ÛÝ[™ÙÙ][�\�˜[ - -HOH ‘‘‘‘‘‘‘ŠNˆ[\[YHHÝ[YNˆ[\™]Ý[YHH™]Ý[YNˆÝ[YHH ˆZ[\ÙXÛÛ™ÈH ˆ\Ý[YHH ˆ˜XÚÙÜ›Ý[™H•S‚ˆYŠJ™\Ý[H[šYÚY—ÛÜ[Šš[[˜[YKXÚÙš[K[™›ÊJJBˆˆÛÝÈ^YÚY—Ù[™ˆB‚ˆÚ[JZ[™›ËO™Û™JBˆˆYŠZ[\ÙXÛÛ™È�H[™›ËOš[™›ÖÌK›™^œ˜[YJBˆˆ[šYÚY—ÙXÛÙWÙœ˜[YJ[™›ÊNˆBˆYŠJ˜XÚØ�Y™™\ˆH[šYÚY—ÙÙ]�Y™™\Š[™›ÊJJBˆˆœ™XZΈBˆÜš]\WØYÜØÜ™Y[Š K ˜XÚØ�Y™™\‹•S  -NˆYŠ[™›ËO™œ˜[YHOH -Bˆˆ™ØWÝ�ØZ] - -Nˆ\]J  -NˆBˆ[ÙBˆˆ\]J  JNˆB‚ˆYŠÞ[˜ÝÜÛÝ[™ -BˆˆZ[\ÙXÛÛ™È -ÏHÛÝ[™ÙÙ][�\�˜[ - -NˆBˆ[ÙBˆˆZ[\ÙXÛÛ™È -ÏH -Ý[YH H\Ý[YJH -ˆ L  ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYYˆB‚ˆ\Ý[YHHÝ[YN‚ˆYŠ[›ÜÚÚ\ ‰ˆ -›Ý™]ÚÙ^\È ˆ -“Q×ÑTÐÈ“Q×ÐS–P•UÓŠJJBˆˆ™\Ý[H LNˆœ™XZΈBˆB‚‚œ^YÚY—Ù[™‚ˆ[šYÚY—ØÛÜÙJ[™›ÊNˆœ™YJ[™›ÊNˆÝ[YHH[\[YNˆ™]Ý[YHH[\™]Ý[YNˆ˜XÚÙÜ›Ý[™H[\™Îˆ ËÜÝ[™\™Ü[]J JNÈ ËÒܘ]\È - KLŒ � -H\ØX›YÈš^HœÈ›Ü\ÜÝYB‚ˆYŠ OH™\Ý[ -Bˆˆš[�Š—•Ø\›š[™Ë[ˆ\œ›ÜˆØØÝ\œ™YÚ[H^Z[™È[š[X]YÚYˆš[H É\ÉË—ˆ‹š[[˜[YJNˆBˆ™]\›ˆ™\Ý[‚ŸB‚‚ˆÚY™YˆÑP“B‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L LL‚Š‚Šˆ™\Ù\�™H›ØÚÚ[™È^]ÙX›J -H™Z]š[܈\ÈHܘ\\ˆ\›Ý[™ŠˆH™]\ØX›H[ÝšYHT\ˈ™]\›ˆ HÛˆÛÛ\][Û‹ Ûˆ\œ›Ü‹Šˆ܈ LHÚ[ˆÜ™X]Ü‹Y[˜X›Y[œ][�\œ�\È^X˜XÚË‚Š‹Âš[�^]ÙX›JÛÛœÝÚ\ˆ -œ] [�›ÜÚÚ\ -BžÂˆ[�™]˜[H Nˆ[�ÛÝ\˜ÙWÚYH LNˆ×Û[ÝšYWÜ^X˜XÚÈ -œ^X˜XÚÈH•Sˆ×ÜØÜ™Y[ˆ -œØÜ™Y[œÚÝÙœ˜[YHH•S‚ˆ[ÝšYWÜ^X˜XÚ×ÜÝÜØ[ - -NˆÛÝ\˜ÙWÚYH[ÝšYWÜÛÝ\˜ÙWÛØY -] SÕ’QWÓÐQS‘×ÔÕ‘PSJNˆYŠÛÝ\˜ÙWÚY -Hˆ™]\›ˆ ˆBˆ^X˜XÚÈH[ÝšYWÜ^X˜XÚ×Ü^JˆÛÝ\˜ÙWÚY ˆSÕ’QWÐÒS“‘SÐUUˈØ]™Y]K›]\ÚXÝ›Û ˆ�YBˆ -NˆYŠ\^X˜XÚÊHˆ™]˜[H ˆÛÝÈ]Z]ˆBˆYŠ[[ÝšYWÜ^X˜XÚ×ÜÙ]Ú[�\œ�\ -^X˜XÚË[›ÜÚÚ\ -JHˆ™]˜[H ˆÛÝÈ]Z]ˆB‚ˆ ʈYØXÞH^X˜XÚÈ\Ù\ÈHÙX“H\Ü^HÚ^™H[™\™Ø\™HUUˆÝ]] ˆ -‹Âˆ[ÝšYWÜ^X˜XÚ×ÜÙ]ÝÚY -^X˜XÚËSÕ’QWÔÒV‘WÓ�UU‘JNˆ[ÝšYWÜ^X˜XÚ×ÜÙ]ÚZYÚ -^X˜XÚËSÕ’QWÔÒV‘WÓ�UU‘JN‚ˆÚ[J^X˜XÚËO˜XÝ]™JHˆ[�[�\œ�\Ü™\]Y\ÝYˆ[�™\Ù[�Ùœ˜[YN‚ˆ[œ]™Yœ™\Ú -^\™XÜÝ]\ËOœÝ]\ÊNˆ[�\œ�\Ü™\]Y\ÝYBˆ -›Ý™]ÚÙ^\È ˆ -“Q×ÑTÐÈ“Q×ÐS–P•UÓŠJHOH ˆYŠ[�\œ�\Ü™\]Y\ÝY ‰ˆ^X˜XÚËOš[�\œ�\ -Hˆ™]˜[H LNˆBˆ[ÝšYWÜ^X˜XÚ×Ý\]J[�\œ�\Ü™\]Y\ÝY -NˆYŠ\^X˜XÚËO˜XÝ]™JHˆœ™XZΈBˆ™\Ù[�Ùœ˜[YHH^X˜XÚËO™œ˜[YWÙ\�NˆYŠ™\Ù[�Ùœ˜[YJHˆYŠ[[ÝšYWÜ^X˜XÚ×Ù˜]×Ý×Þ]]Š^X˜XÚËOš[™^ -JHˆ™]˜[H ˆœ™XZΈBˆBˆYŠ^X˜XÚËO˜Ý\œ™[�Ùœ˜[YH ‰‚ˆ[›ÜØÜ™Y[œÚÝ ‰‚ˆ -›Ý™]ÚÙ^\È ˆ“Q×ÔÐÔ‘QS”ÒÕ -JHˆYŠ\ØÜ™Y[œÚÝÙœ˜[YHˆØÜ™Y[œÚÝÙœ˜[YKO�ÚYOH^X˜XÚËO˜Ý\œ™[�Ùœ˜[YKO�ÚYˆØÜ™Y[œÚÝÙœ˜[YKOšZYÚOH^X˜XÚËO˜Ý\œ™[�Ùœ˜[YKOšZYÚ -HˆYŠØÜ™Y[œÚÝÙœ˜[YJHˆœ™Y\ØÜ™Y[Š œØÜ™Y[œÚÝÙœ˜[YJNˆBˆØÜ™Y[œÚÝÙœ˜[YHH[ØÜØÜ™Y[Šˆ^X˜XÚËO˜Ý\œ™[�Ùœ˜[YKO�ÚY ˆ^X˜XÚËO˜Ý\œ™[�Ùœ˜[YKOšZYÚ ˆVSÌÌ‚ˆ -NˆBˆYŠØÜ™Y[œÚÝÙœ˜[YJHˆ]]—Ý×ܙ؊^X˜XÚËO˜Ý\œ™[�Ùœ˜[YKØÜ™Y[œÚÝÙœ˜[YJNˆØÜ™Y[œÚÝ -ØÜ™Y[œÚÝÙœ˜[YK•S  -NˆBˆBˆ\ÛY\ - L -NˆB‚œ]Z]‚ˆYŠØÜ™Y[œÚÝÙœ˜[YJHˆœ™Y\ØÜ™Y[Š œØÜ™Y[œÚÝÙœ˜[YJNˆBˆYŠ^X˜XÚÈ ‰ˆ^X˜XÚËO™˜Z[Y ‰ˆ™]˜[ˆ -Hˆ™]˜[H ˆBˆYŠ^X˜XÚÈ ‰ˆ^X˜XÚËO˜XÝ]™JHˆ[ÝšYWÜ^X˜XÚ×ÜÝÜ -^X˜XÚÊNˆBˆÚ[JÛÝ\˜ÙWÚY�H  ‰ˆ[[ÝšYWÜÛÝ\˜ÙWÝ[›ØY -ÛÝ\˜ÙWÚY -JHˆ[ÝšYWÜ^X˜XÚ×Ý\]J -Nˆ\ÛY\ - L -NˆBˆ™]\›ˆ™]˜[ŸBˆÙ[™Y‚‚‚‚�›ÚY^\ØÙ[™JÚ\ˆ -™š[[˜[YJBžÂˆÚ\ˆ -˜�YŽÂˆÚ^™WÝÚ^™Nˆ[�ÜΈÚ\ˆ -˜ÛÛ[X[™H•SˆÚ\ˆšY[Ùš[VÓPVЕQ‘‘T—ÓS—Nˆ[�H HH ÚÚ\Û™HH ›ÜÚÚ\H Nˆ[�ÛÜÚ[™ÈH Ý]\΂ˆ\™Ó\Ý\™Û\݈Ú\ˆ\™Ø�Y–ÓPVÐT‘×ÓSˆ -È WHHˆŽÂ‚ˆ ËÈ™XYš[BˆYŠ�Y™™\—ÜZÙš[Jš[[˜[YK ˜�Y‹ œÚ^™JHOH JBˆˆ™]\›ŽÂˆB‚ˆÝ\œ™[�ØÙ[™HHš[[˜[YN‚ˆ ËÈ›ÝÈ[�\œ™]HÛÛ�[�ÈÙˆ�Yˆ[™HžH[™BˆÜÈH ˆÚ[J�Y–ÜÜ×JBˆˆ\œÙP\™ÜÊ ˜\™Û\Ý �Yˆ -ÈÜË\™Ø�YŠNˆÛÛ[X[™HÑUÐT‘Ê -NˆYŠÛÛ[X[™ÌJBˆˆYŠXÛÜÚ[™È ‰ˆÝšXÛ\ -ÛÛ[X[™ ›]\ÚXÈŠHOH -Bˆˆ]\ÚXÊÑUÐT‘Ê JKÑUÒS•ÐT‘Ê ŠK]Û -ÑUÐT‘Ê ÊJJNˆBˆ[ÙHYŠXÛÜÚ[™È ‰ˆÝšXÛ\ -ÛÛ[X[™ ˜[š[X][ÛˆŠHOH -BˆˆÝ˜ÜJšY[Ùš[KÑUÐT‘Ê JJNˆHÑUÒS•ÐT‘Ê ŠNˆHHÑUÒS•ÐT‘Ê ÊNˆÚÚ\Û™HHÑUÒS•ÐT‘Ê -Nˆ›ÜÚÚ\HÑUÒS•ÐT‘Ê JNˆÝ]\ÈH^YÚYŠšY[Ùš[K K›ÜÚÚ\ -NˆYŠÝ]\ÈOH LH ‰ˆ\ÚÚ\Û™JBˆˆÛÜÚ[™ÈH NˆBˆBˆ[ÙHYŠXÛÜÚ[™È ‰ˆÝšXÛ\ -ÛÛ[X[™ �šY[ÈŠHOH -BˆˆÚY™YˆÑP“BˆÝ˜ÜJšY[Ùš[KÑUÐT‘Ê JJNˆÚÚ\Û™HHÑUÒS•ÐT‘Ê ŠNˆ›ÜÚÚ\HÑUÒS•ÐT‘Ê ÊNˆÝ]\ÈH^]ÙX›JšY[Ùš[K›ÜÚÚ\ -NˆYŠÝ]\ÈOH LH ‰ˆ\ÚÚ\Û™JBˆˆÛÜÚ[™ÈH NˆBˆ[ÙHYŠÝ]\ÈOH -Bˆˆš[�Š�[ˆ\œ›ÜˆØØÝ\œ™YÚ[ˆžZ[™ÈÈ^HHšY[È \׈‹šY[Ùš[JNˆBˆÙ[ÙBˆš[�Š”ÚÚ\[™ÈšY[È \ÎÈÙX“H^X˜XÚÈ›ÝÝ\Ü�YÛˆ\È]›Ü›WˆŠNˆÙ[™Y‚ˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ œÚ[[˜ÙHŠHOH -BˆˆÛÝ[™ØÛÜÙWÛ]\ÚXÊ -NˆBˆBˆ ËÈÛÈÈ™^›Û‹X›[šÈ[™BˆÜÈ -ÏHÙ]™]Ó[™TÝ\� -�Yˆ -ÈÜÊNˆBˆYŠ�YˆOH•S -Bˆˆœ™YJ�YŠNˆ�YˆH•SˆBˆÝ\œ™[�ØÙ[™HH•Sˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆ^Y\–ÚWK™\ØX›ZÙ^\ÈH^Y\–ÚWK›™]ÚÙ^\ÈH^Y\–ÚWKœ^ZÙ^\ÈH ˆBŸB‚‚‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚‚‚‚�›ÚYØ[Y[Ý™\Š -BžÂˆ[�Û™HH ˆÚ\ˆ\�Y™–ÓPVЕQ‘‘T—ÓS—HHȈŸN‚ˆ]\ÚXÊ™]KÛ]\ÚXËÙØ[Y[Ý™\ˆ‹  -N‚ˆÝ[YHH ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÑÐSQWÓÕ‘TŽÂ‚ˆYŠÝ\ÝØÙ[™\ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝØÙ[™\ÊNˆÝ˜Ø] -\�Y™‹™Ø[Y[Ý™\‹�ŠNˆYŠ\ÝXÚÙš[J\�Y™‹XÚÙš[JH�H -Bˆˆ^\ØÙ[™J\�Y™ŠNˆÛ™HH NˆBˆBˆ[ÙBˆˆYŠ\ÝXÚÙš[J™]KÜØÙ[™\ËÙØ[Y[Ý™\‹�‹XÚÙš[JH�H -Bˆˆ^\ØÙ[™J™]KÜØÙ[™\ËÙØ[Y[Ý™\‹�ŠNˆÛ™HH NˆB‚ˆB‚ˆÚ[JYÛ™JBˆˆ›Û�Üš[�ŠÜÝ›ZY - ËŠ‘ÐSQHÕ‘TˆŠJK LL -ÈšY[Û[Ù\Ë�”ÚY�  Ë Š‘ÐSQHÕ‘TˆŠJNˆÛ™HH -Ý[YHˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ‰ˆ\ÛÝ[™Ü]Y\žWÛ]\ÚXÊ•S •S -JNˆÛ™HH -›Ý™]ÚÙ^\È ˆ -“Q×ÑTÐÈ“Q×ÐS–P•UÓŠJNˆ\]J  -NˆB‚ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÑÐSQWÓÕ‘TŽÂŸB‚‚‚‚�›ÚY[˜[YJ[�YÜØÛÜ™JBžÂˆ[�Û™HH ˆ[�Ü[–ÌLHHÈ          NˆZ[� �ÝØÛÜ™NˆÚ\ˆ˜[YVÓPVÓ�SQWÓSˆ -È WNˆ[�K NˆÚ\ˆ\�Y™–ÓPVЕQ‘‘T—ÓS—HHȈŸNˆ[�ÛÛ HH Nˆ[�ÛÛ ˆH ŽÂ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÒSÓÑ—Ñ�SQN‚ˆYŠ\ØÛÜ™X™ÊBˆˆ ËÈ™]È[\›˜]]™H˜XÚÙÜ›Ý[™] ‚ˆYŠÝ\ÝšÙÜ™ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝšÙÜ™ÊNˆÝ˜Ø] -\�Y™‹š\ØÛÜ™HŠNˆØYؘXÚÙÜ›Ý[™ -\�Y™ŠNˆBˆ[ÙBˆˆØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÚ\ØÛÜ™HŠNˆBˆB‚ˆYŠYÜØÛÜ™JBˆˆ›ÜŠH È]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈ -ÊÊBˆˆYŠ^Y\–ÜKœØÛÜ™HˆØ]™\ØÛÜ™KšYÚØÖÎWJBˆˆØ]™\ØÛÜ™KšYÚØÖÎWHH^Y\–ÜKœØÛÜ™NˆÝ˜ÜJØ]™\ØÛÜ™KšØÛÜ™[–ÎWK^Y\–ÜK›˜[YJNˆÜ[–ÎWHH N‚ˆ›ÜŠHHÈH�H  ‰ˆ^Y\–ÜKœØÛÜ™HˆØ]™\ØÛÜ™KšYÚØÖÚWNÈKKJBˆˆØÛÜ™HHØ]™\ØÛÜ™KšYÚØÖÚWNˆÝ˜ÜJ˜[YKØ]™\ØÛÜ™KšØÛÜ™[–ÚWJNˆØ]™\ØÛÜ™KšYÚØÖÚWHH^Y\–ÜKœØÛÜ™NˆÝ˜ÜJØ]™\ØÛÜ™KšØÛÜ™[–ÚWK^Y\–ÜK›˜[YJNˆÜ[–ÚWHH NˆØ]™\ØÛÜ™KšYÚØÖÚH -È WHHØÛÜ™NˆÝ˜ÜJØ]™\ØÛÜ™KšØÛÜ™[–ÚH -È WK˜[YJNˆÜ[–ÚH -È WHH ˆBˆBˆBˆØ]™RYÚØÛÜ™Qš[J -NˆB‚ˆÝ[YHH ‚ˆÚ[JYÛ™JBˆˆHH MŽÂˆYŠZ\ØÛÜ™X™ÊBˆˆ›Û�Üš[�ŠÜÝ›ZY - ËŠ’[Ùˆ˜[YHŠJKH H›Û�ZYÚ - ÊH H L -ÈšY[Û[Ù\Ë�”ÚY�  Ë Š’[Ùˆ˜[YHŠJNˆB‚ˆ›ÜŠHH ÈH LÈJÊÊBˆˆ›Û�Üš[�ŠØÛÛ -Ü[–ÚWKÛÛ JKH -ÈšY[Û[Ù\Ë�”ÚY� Ü[–ÚWK ‰LšKˆ \È‹H -È KØ]™\ØÛÜ™KšØÛÜ™[–ÚWJNˆ›Û�Üš[�ŠØÛÛ -Ü[–ÚWKÛÛ ŠKH -ÈšY[Û[Ù\Ë�”ÚY� Ü[–ÚWK  -ØÛÜ™Y›Ü›X]ȉLHˆ’]M�ˆ‰Hˆ’]M� -KØ]™\ØÛÜ™KšYÚØÖÚWJNˆH -ÏH -šY[Û[Ù\Ë�”™\È HšY[Û[Ù\Ë�”ÚY� H Mˆ H ÌŠH È LÈ ËÙ›Û�ÚZYÚÖÝÜ[–ÚWWH -È ŽÂˆB‚ˆ ËÈܘ]\È - KLŒ ŒÊHYYH‘“Q×ÐS–P•UÓˆˆÈ^]H[Ùˆ˜[YHØÜ™Y[‚ˆ\]J  -NˆÛ™HH -Ý[YHˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ -NˆÛ™HH -›Ý™]ÚÙ^\È ˆ -“Q×ÔÕT•“Q×ÐS–P•UÓˆ“Q×ÑTÐÊJNˆBˆ[›ØYؘXÚÙÜ›Ý[™ - -NˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÒSÓÑ—Ñ�SQNŸB‚‚‚‚‹ËÈ]™[ÛÛ\]Y ÚÝÈ›Û�\ÈÝY™‚�›ÚYÚÝØÛÛ\]J[��[JBžÂˆ[�Û™HH ˆ[�K‹ΈL̈ÛX\˜›Û�\ÖÍHHÈ L  L  L  L NˆL̈Y™X›Û�\ÖÍHHÈ L  L  L  L NˆL̈�\Ú›Û�\ÖÍHHÈ L  L  L  L NˆL̈™^[YHH ˆL̈š[š\Ú[YHH ˆ[�Ú[ˆH ˆÚ\ˆ\�Y™–ÓPVЕQ‘‘T—ÓS—HHȈŸN‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÔÒÕ×ÐÓÓTUN‚ˆYŠÛÛ\]X™ÊBˆˆ ËÈ™]È[\›˜]]™H˜XÚÙÜ›Ý[™] ‚ˆYŠÝ\ÝšÙÜ™ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝšÙÜ™ÊNˆÝ˜Ø] -\�Y™‹˜ÛÛ\]HŠNˆØYؘXÚÙÜ›Ý[™ -\�Y™ŠNˆBˆ[ÙBˆˆØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËØÛÛ\]HŠNˆBˆB‚ˆ]\ÚXÊ™]KÛ]\ÚXËØÛÛ\]H‹  -N‚ˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ�\ÚÌH�H H ‰ˆÚÝÜ�\Ú›Û�\ÈOH JBˆˆ�\Ú›Û�\ÖÚWHH›ÛX^�\Ú™\Ù]ÚWH -ˆØØ›Û�\Ù\ÖÌ—NˆBˆYŠØØ›Û�\Ù\ÖÌ×HOH JBˆˆÛX\˜›Û�\ÖÚWHH�[H -ˆØØ›Û�\Ù\ÖÌNˆBˆ[ÙBˆˆÛX\˜›Û�\ÖÚWHHØØ›Û�\Ù\ÖÌNˆBˆY™X›Û�\ÖÚWHH^Y\–ÚWK›]™\È -ˆØØ›Û�\Ù\ÖÌWNˆB‚ˆ\]J  -N‚ˆÝ[YHH ˆÚ[JYÛ™JBˆˆYŠ\ØÛÛ\]VÍWJBˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÛ\]VÌKšY[Û[Ù\Ë�”ÚY� -ÈØÛÛ\]VÌWK Ë Š”ÝYÙH ZHÛÛ\]HHŠK�[JNˆBˆ[ÙBˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÛ\]VÌKšY[Û[Ù\Ë�”ÚY� -ÈØÛÛ\]VÌWK Ë Š”ÝYÙHŠJNˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÛ\]VÌ—KšY[Û[Ù\Ë�”ÚY� -ÈØÛÛ\]VÌ×K Ë ‰ZH‹�[JNˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÛ\]VÍKšY[Û[Ù\Ë�”ÚY� -ÈØÛÛ\]VÍWK Ë Š�ÛÛ\]HŠJNˆB‚ˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØ›Û�\ÖÌKšY[Û[Ù\Ë�”ÚY� -ÈØ›Û�\ÖÌWK  Š�ÛX\ˆ›Û�\ÈŠJNˆ›ÜŠHH ˆH ‹ÈH ÎÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊˈHˆ -È ‹ÈHÈ -È ŠHYŠ^Y\–ÚWK›]™\Ȉ -Bˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØ›Û�\ÖÚ—KšY[Û[Ù\Ë�”ÚY� -ÈØ›Û�\ÖÚ×K   -ØÛÜ™Y›Ü›X]ȉL[Hˆˆ‰[HŠKÛX\˜›Û�\ÖÚWJNˆBˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -È›Û�\ÖÌKšY[Û[Ù\Ë�”ÚY� -È›Û�\ÖÌWK  Š“Y™H›Û�\ÈŠJNˆ›ÜŠHH ˆH ‹ÈH ÎÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊˈHˆ -È ‹ÈHÈ -È ŠHYŠ^Y\–ÚWK›]™\Ȉ -Bˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -È›Û�\ÖÚ—KšY[Û[Ù\Ë�”ÚY� -È›Û�\ÖÚ×K   -ØÛÜ™Y›Ü›X]ȉL[Hˆˆ‰[HŠKY™X›Û�\ÖÚWJNˆBˆYŠ�\ÚÌH�H H ‰ˆÚÝÜ�\Ú›Û�\ÈOH JBˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -Ș›Û�\ÖÌKšY[Û[Ù\Ë�”ÚY� -Ș›Û�\ÖÌWK  Š”�\Ú›Û�\ÈŠJNˆ›ÜŠHH ˆH ‹ÈH ÎÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊˈHˆ -È ‹ÈHÈ -È ŠHYŠ^Y\–ÚWK›]™\Ȉ -Bˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -Ș›Û�\ÖÚ—KšY[Û[Ù\Ë�”ÚY� -Ș›Û�\ÖÚ×K   -ØÛÜ™Y›Ü›X]ȉL[Hˆˆ‰[HŠK�\Ú›Û�\ÖÚWJNˆBˆBˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÜ™VÌKšY[Û[Ù\Ë�”ÚY� -ÈØÛÜ™VÌWK  Š•Ý[ØÛÜ™HŠJNˆ›ÜŠHH ˆH ‹ÈH ÎÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊˈHˆ -È ‹ÈHÈ -È ŠHYŠ^Y\–ÚWK›]™\Ȉ -Bˆˆ›Û�Üš[�ŠšY[Û[Ù\ËšÚY� -ÈØÛÜ™VÚ—KšY[Û[Ù\Ë�”ÚY� -ÈØÛÜ™VÚ×K   -ØÛÜ™Y›Ü›X]ȉLHˆ’]M�ˆ‰Hˆ’]M� -K^Y\–ÚWKœØÛÜ™JNˆB‚ˆÚ[JÝ[YHˆ™^[YJBˆˆYŠYš[š\Ú[YJBˆˆš[š\Ú[YHHÝ[YH -È  -ˆÛؘ[ØÛÛ™šYË™Ø[YWÜÜYYˆB‚ˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK›]™\Ȉ -BˆˆYŠÛX\˜›Û�\ÖÚWHˆ -BˆˆYØÛÜ™JK L -NˆÛX\˜›Û�\ÖÚWH OH Lˆš[š\Ú[YHH ˆBˆ[ÙHYŠY™X›Û�\ÖÚWHˆ -BˆˆYØÛÜ™JK L -NˆY™X›Û�\ÖÚWH OH Lˆš[š\Ú[YHH ˆBˆ[ÙHYŠ�\ÚÌH�H H ‰ˆÚÝÜ�\Ú›Û�\ÈOH H ‰ˆ -�\Ú›Û�\ÖÚWHˆ -JBˆˆYØÛÜ™JK L -Nˆ�\Ú›Û�\ÖÚWH OH Lˆš[š\Ú[YHH ˆBˆBˆB‚ˆYŠYš[š\Ú[YH ‰ˆJ™^[YH ˆ MJJBˆˆÛÝ[™ÜÝÜÜØ[\JÚ[ŠNˆÚ[ˆHÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û È ‹Ø]™Y]K™Y™™XÝ›Û È ‹ L -NˆBˆ™^[YJÊΈB‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÐS–P•UÓˆ“Q×ÑTÐÊJBˆˆÛ™HH NˆBˆYŠš[š\Ú[YH ‰ˆÝ[YHˆš[š\Ú[YJBˆˆÛ™HH NˆB‚ˆ\]J  -NˆB‚ˆ ËÈY™[XZ[™\ˆÙˆØÛÜ™K[˜Ø\ÙH^Y\ˆÚÚ\ÈÛÝ[�\‚ˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK›]™\Ȉ -BˆˆYŠ�\ÚÌH�H H ‰ˆÚÝÜ�\Ú›Û�\ÈOH JBˆˆYØÛÜ™JK�\Ú›Û�\ÖÚWJNˆBˆYØÛÜ™JKÛX\˜›Û�\ÖÚWJNˆYØÛÜ™JKY™X›Û�\ÖÚWJNˆBˆBˆ[›ØYؘXÚÙÜ›Ý[™ - -N‚ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÔÒÕ×ÐÓÓTUNŸB‚�›ÚYØ]™[]™[[™›Ê -BžÂˆ[�Nˆ×ÜÙ]Ù[�žH -œÙ]H]™[Ù]È -ÈÝ\œ™[�ÜÙ]ˆ×ÜØ]™[]™[ -œØ]™HHØ]™[]™[ -ÈÝ\œ™[�ÜÙ]‚ˆØ]™KO™›YÈHÙ] OœØ]™Y›YΈ ËÈÛ‰ÝÚXÚÈ›YÈ\™HØ]™H[[™›Ë›ÜˆÚ[\HÙÚXˆ›ÜŠHH ÈHÙ] O›X^^Y\œÎÈJÊÊBˆˆØ]™KOœ]™\ÖÚWHH^Y\–ÚWK›]™\ΈØ]™KOœÜ™Y]ÖÚWHH^Y\–ÚWK˜Ü™Y]ΈØ]™KOœØÛÜ™\ÖÚWHH^Y\–ÚWKœØÛÜ™NˆØ]™KOœÜ]ÛšX[ÚWHH^Y\–ÚWKœÜ]ÛšX[ˆØ]™KOœÜ]Û›\ÚWHH^Y\–ÚWKœÜ]Û›\ˆØ]™KOœÙX\�[VÚWHH^Y\–ÚWK�ÙX\�[NˆØ]™KOœÛÛÝ\›X\ÚWHH^Y\–ÚWK˜ÛÛÝ\›X\ˆÝ›˜ÜJØ]™KOœ˜[YVÚWK^Y\–ÚWK›˜[YKPVÓ�SQWÓSŠNˆBˆØ]™KO˜Ü™Y]ÈHÜ™Y]ΈØ]™KO›]™[HÝ\œ™[�Û]™[ˆØ]™KOœÝYÙHHÝ\œ™[�ÜÝYÙNˆØ]™KO�ÚXÚÜÙ]HÝ\œ™[�ÜÙ]ˆÝ›˜ÜJØ]™KO™˜[YKÙ] O›˜[YKPVÓ�SQWÓSˆ H JNˆÙ]ÜØ]™YØ[ÝÜÙ[XÝØ\™Ý[Y[�ÊÝ\œ™[�ÜÙ] [ÝÜÙ[XÝØ\™ÜÊNŸB‚�›ÚYž]šXÝÜž\ÜÙJ[�]H -™[� -BžÂˆYŠ[� ‰‚ˆ[� Oš[œZ[ˆ ˆ’S—ÔRS—Ó“Ó‘H ‰‚ˆY[� O™˜[[™È ‰‚ˆJ[� O™X]ÜÝ]H ˆPUÔÕUWÑPQ -H ‰‚ˆY[� Oœš\Ú[™È ‰‚ˆ -[� OšY[™È ˆQS‘×ÐPÕU‘JH ‰‚ˆ[� OœÜÚ][Û‹žHH[� O˜˜\ÙH -Bˆˆ[� O�ZÙXXÝ[ÛˆH•Sˆ[� O�™[ØÚ]KžH[� O�™[ØÚ]KžˆH ˆ[� OšY[™ÈHQS‘×Ó“Ó‘Nˆ[�ÜÙ]Ø[š[J[� S’WÕ’PÕÔ–K -NˆBŸB‚œÝ]XÈ›ÚYÚXÚ×ÝšXÝÜžWÜÜÙJ -BžÂˆYˆ - -[™Ø[YH ˆ JH ‰ˆ]™[ØÛÛ\]YÙY™X][™×Ø›ÜÜÈ -Bˆˆ[�Nˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆ[�]Jˆ\HÙ[ŽÂˆÙ[ˆH^Y\–ÚWK™[�ˆYŠÙ[ˆ ‰ˆ˜[Y[š[JÙ[‹S’WÕ’PÕÔ–JH ‰ˆÙ[‹O˜[š[[�[HOHS’WÕ’PÕÔ–JBˆˆž]šXÝÜž\ÜÙJÙ[ŠNˆ[™Ø[YHH ˆBˆ[ÙHYˆ -Ù[ˆ ‰ˆÙ[‹O˜[š[[�[HOHS’WÕ’PÕÔ–H ‰ˆÙ[‹O˜[š[X][™ÊBˆˆ[™Ø[YHH ˆB‚ˆÙ[ˆH\ˆBˆBŸB‚š[�^[]™[ -Ú\ˆ -™š[[˜[YJBžÂˆ[�K\KØ[]™HH ‚ˆÚ[Ø[ - -N‚ˆØ]™[]™[[™›Ê -NÈ ËÈ�\Ý[ˆØ\ÙHÙHÜÙH[HY�\ˆ]™[\Èœ™YY‚ˆØYÛ]™[ -š[[˜[YJN‚ˆYŠ[›ÜØ]™JBˆˆØ]™QØ[YQš[J -NˆØ]™RYÚØÛÜ™Qš[J -NˆØ]™TØÜš\š[J -NˆBˆ›ÜØ]™HH ‚ˆÝ[YHH ˆ™^[ˆH ˆÝ[Ù\ˆH•Sˆš\œÝ^Y\ˆH•Sˆ\HH]™[ O�\N‚ˆ ËÈš^\ÈHÝ\�]™[^XÝ][™È\Ý�]Ûˆ�Yˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK›]™\Ȉ -Bˆˆ^Y\–ÚWK™\ØX›ZÙ^\ÈH^Y\–ÚWK›™]ÚÙ^\ÈH^Y\–ÚWKœ^ZÙ^\ÈH ˆ^Y\–ÚWK�ÙX\�[HH]™[ OœÙ]ÙX\ˆ^Y\–ÚWKš›Ú[š[™ÈH ˆ^Y\–ÚWKš\Ü^YYH NˆÜ]Ûœ^Y\ŠJNˆ^Y\–ÚWK™[� Oœ�\Ú ›X^H ˆBˆB‚ˆ ËÙ^XÝ]HHØÜš\Ú[ˆ]™[Ý\�YˆYŠØÜš\Ò\Ò[š]X[^™Y - ›]™[ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J ›]™[ÜØÜš\ -NˆBˆYŠØÜš\Ò\Ò[š]X[^™Y - Š]™[ O›]™[ÜØÜš\ -JJBˆˆØÜš\Ñ^XÝ]J Š]™[ O›]™[ÜØÜš\ -JNˆB‚ˆÚ[JY[™Ø[YJBˆˆ\]J K -N‚ˆYˆ -]™[ O™›Ü˜ÙWÙš[š\Ú]™[ -Bˆˆ]™[ØÛÛ\]YH Nˆ[™Ø[YHH Nˆ]™[ O™›Ü˜ÙWÙš[š\Ú]™[H ˆBˆYˆ -]™[ O™›Ü˜ÙWÙØ[Y[Ý™\ŠBˆˆ ËÙ[�]Jˆ[\HÙ[ŽÂˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊH ËÛ]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÂˆˆ^Y\–ÚWK›]™\ÈH ˆYŠ›ÜÚ\™JBˆˆ^Y\–ÚWK˜Ü™Y]ÈH ˆBˆ[ÙBˆˆÜ™Y]ÈH ˆBˆYˆ -^Y\–ÚWK™[� -B‚BBB^‚BBBBZÚ[Ù[�]J^Y\–ÚWK™[� ÒSÑS•UWÕ’QÑÑT—ÓU‘SÑÐSQWÓÕ‘TŠN‚BBBB\^Y\–ÚWK™[�H•S‚BBB_Bˆ ËÜÙ[ˆH^Y\–ÚWK™[�ˆ ËÜ^Y\—ÙYJ -NˆBˆ ËÜÙ[ˆH[\ˆ ËÚÚ[Ø[ - -Nˆ[™Ø[YHH Nˆ]™[ O™›Ü˜ÙWÙØ[Y[Ý™\ˆH ˆBˆYŠ]™[ØÛÛ\]Y -Bˆˆ[™Ø[YHH -Yš[™[� -TWÑS‘SVJH]™[ O�\Hš[™[� -TWÑS‘U‘S -JNÈ ËÈ[™ÈÚ[ˆ[[™[ZY\ÈYH܈H›Û�\È]™[ˆÚXÚ×ÝšXÝÜžWÜÜÙJ -NˆBˆBˆ ËÙ^XÝ]HHØÜš\Ú[ˆ]™[š[š\ÚYˆYŠØÜš\Ò\Ò[š]X[^™Y - ™[™]™[ÜØÜš\ -JBˆˆØÜš\Ñ^XÝ]J ™[™]™[ÜØÜš\ -NˆBˆYŠØÜš\Ò\Ò[š]X[^™Y - Š]™[ O™[™]™[ÜØÜš\ -JJBˆˆØÜš\Ñ^XÝ]J Š]™[ O™[™]™[ÜØÜš\ -JNˆBˆYŠ[›Ù˜Y[Ý] -Bˆˆ˜YWÛÝ] -  -NˆB‚ˆ›ÜŠHH ÈH]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÎÈJÊÊBˆˆYŠ^Y\–ÚWK™[� -Bˆˆ›ÛX^�\Ú™\Ù]ÚWHH^Y\–ÚWK™[� Oœ�\Ú ›X^ˆ^Y\–ÚWKœÜ]ÛšX[H^Y\–ÚWK™[� O™[™\™ÞWÜÝ]KšX[ØÝ\œ™[�ˆ^Y\–ÚWKœÜ]Û›\H^Y\–ÚWK™[� O™[™\™ÞWÜÝ]K›\ØÝ\œ™[�ˆBˆ ËÈ™\Ù]ˆ^Y\–ÚWK�ÙX\�[HH ˆB‚ˆYŠ[]\ÚXÛÝ™\›\ -BˆˆÛÝ[™ØÛÜÙWÛ]\ÚXÊ -NˆBˆÛÝ[™ÜÝÜ[ÜØ[\J˜[ÙJN‚ˆ[›ØYÛ]™[ - -N‚ˆ ËÈ\™H[žH^Y\œÈ[]™O‚Y›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -^Y\–ÚWK›]™\Ȉ -BˆˆØ[]™HH Nˆœ™XZΈBˆB‚ˆ™]\›ˆ - -\HOH ˆ ‰ˆ[™Ø[YHOH ŠHØ[]™JNŸB‚‹ËÈØ\ÚÙ^K[[Ûˆˆ -™]ÛÛ ÐH[šÛ›ÝÛŠB‹ËÈ Œ NKL KL ‹Ë‹Ëț܈Ù[XÝØÜ™Y[‹ˆÜ]ÛˆØ[\H[�]H›Üˆ^Y\—Ú[™^ ‚œÝ]XÈ[�]H -œÜ]Û™^[\J[�^Y\—Ú[™^ -BžÂˆÚ\ŠˆÔUÓ—ÓSÑSÓ�SQHH•SˆÛÛœÝ[�ÔUÓ—ÓSÑSÒS‘VHSÑSÒS‘VÓ“Ó‘N‚ˆ[�]BJ™^[\N‚\×Û[Ù[ -›[Ù[‚\×ÜÙ]Ù[�žH -œÙ]‚‚Y›Ø]Ü×Þ‚Y›Ø]Ü×ÞN‚Y›Ø]Ü×ގ‚Z[�\™XÝ[ێ‚BB‚\Ù]H]™[Ù]È -ÈÝ\œ™[�ÜÙ]‚‚KËÈÙ]Ü]Ûˆ]šX�]\È[™Ü]Ûˆ[�]K‚‚\Ü×ÞH -›Ø] -\ÛY[�VÜ^Y\—Ú[™^VÌN‚\Ü×ÞHH ‚\Ü×ÞˆH -›Ø] -\ÛY[�VÜ^Y\—Ú[™^VÌWN‚Y\™XÝ[ÛˆHÜ\™XÝ[Û–Ü^Y\—Ú[™^N‚‚KËÈ™^Ù[XÝX›H[Ù[[ˆÞXÛKˆÙIÛ\ÙH\‚KËÈÈXÚYHÚ]ÙHÜ]Û‹‚‚[[Ù[H™^^Y\›[Ù[Š•S ^Y\—Ú[™^ -N‚‚Y^[\HHÜ]ÛŠÜ×Þ Ü×Þ‹Ü×ÞK\™XÝ[Û‹ÔUÓ—ÓSÑSÓ�SQKÔUÓ—ÓSÑSÒS‘V [Ù[ -Nˆ‚KËÈÛÜH[Ù[ ÜȘ[YHÈ^Y\ˆ›Ü\�K‚‚\ݘÜJ^Y\–Ü^Y\—Ú[™^K›˜[YK[Ù[ O›˜[YJN‚‚KËÈYˆÛÛ܈Ù[XÝ[Ûˆ\È[ÝÙY[™ÙHØ[�‚KËÈ^Y\œÈÚ]Ø[YH[Ù[ÈÈ\ÙHY™™\™[�‚KËÈX\Ë[ˆ\ÙH™^X\[ˆÞXÛKˆÝ\�Ú\ÙB‚KËÈ�\ÝÛÈÚ]Y˜][X\ ‚‚ZYˆ -ÛÛÝ\œÙ[XÝ ‰ˆ -Ù] O››ÜØ[YH ˆ ŠJB‚^‚B\^Y\–Ü^Y\—Ú[™^K˜ÛÛÝ\›X\H™^ÛÛÝ\›X\Š[Ù[  LK^Y\—Ú[™^ -N‚_B‚Y[ÙB‚^‚B\^Y\–Ü^Y\—Ú[™^K˜ÛÛÝ\›X\H ‚_B‚‚KËÈ\HX\ÈÜ]Û™Y[�]K‚ˆ[�ÜÙ]ØÛÛÝ\›X\ -^[\K^Y\–Ü^Y\—Ú[™^K˜ÛÛÝ\›X\ -Nˆ‚KËÈÛÈH[�]HÛ›ÝÜÈÝÈ]Ø[YHÈ™K‚‚Y^[\KOœÜ]Û�\HHÔUÓ—ÕTWÔVQT—ÔÑSPÕˆ‚\™]\›ˆ^[\NŸB‚‹ËÈØYØ]™YÙ[XÝØÜ™Y[‚œÝ]XÈ›ÚYØYÜÙ[XÝÜØÜ™Y[—Ú[™›Ê×ÜØ]™[]™[ -œØ]™JBžÂˆ[�HH ˆ\™Ó\Ý\™Û\݈Ú\ˆ\™Ø�Y–ÓPVÐT‘×ÓSˆ -È WHHˆŽÂˆÚ\ˆ -™š[[˜[YHHˆŽÈ ËÈ›Ý\ÙY �\Ý�\ÙY[�ÈÑUÒS•ÐT‘Ê -BˆÚ\ˆ -˜ÛÛ[X[™HˆŽÈ ËÈ›Ý\ÙY �\Ý�\ÙY[�ÈÑUÒS•ÐT‘Ê -B‚ˆ ËÈØY[Ù[ÛØYˆ›ÜŠHH ÈHØ]™KOœÙ[XÝØYÛÝ[�ÈJÊÊBˆˆ×Û[Ù[ -�[\[Ù[ˆÛÛ[X[™HÑUÐT‘Ê -N‚ˆYŠXÛÛ[X[™XÛÛ[X[™ÌJHÛÛ�[�YNˆ\œÙP\™ÜÊ ˜\™Û\Ý Ø]™KOœÙ[XÝØYÚWK\™Ø�YŠN‚ˆ[\[Ù[Hš[™[Ù[ -ÑUÐT‘Ê JJNˆYˆ -[\[Ù[ -Bˆˆ\]WÛ[Ù[ÛØY›YÊ[\[Ù[ ÑUÒS•ÐT‘Ê ŠJNˆBˆB‚ˆ\œÙP\™ÜÊ ˜\™Û\Ý Ø]™KOœÙ[XÝ]\ÚXË\™Ø�YŠNˆÛÛ[X[™HÑUÐT‘Ê -NˆYŠÛÛ[X[™ ‰ˆÛÛ[X[™ÌJH]\ÚXÊÑUÐT‘Ê JKÑUÒS•ÐT‘Ê ŠK]Û -ÑUÐT‘Ê ÊJJN‚ˆ\œÙP\™ÜÊ ˜\™Û\Ý Ø]™KOœÙ[XݘXÚÙÜ›Ý[™ \™Ø�YŠNˆÛÛ[X[™HÑUÐT‘Ê -NˆYŠÛÛ[X[™ ‰ˆÛÛ[X[™ÌJHØYؘXÚÙÜ›Ý[™ -ÑUÐT‘Ê JJN‚ˆ™]\›ŽÂŸB‚š[�Ù[XÝ^Y\Š[� -œ^Y\œËÚ\ˆ -™š[[˜[YK[�\ÙTØ]™YØ[YJBžÂ‚\×Û[Ù[ -�[\[Ù[‚\×Û[Ù[ -›[Ù[Û™]ÖÓPVÔVQT”×HHÈ•SN‚Z[�N‚Z[�^]H ‚Z[�\ØØ\HH ‚Z[�Y˜][Ù[XÝH ‚][œÚYÛ™Y^][^HH ‚Z[�^Y\œ×Ø�\ÞHH ‚Z[�^Y\œ×Ü™XYHH ‚XÚ\ˆÝš[™ÖÓPVЕQ‘‘T—ÓS—HHȈˆN‚XÚ\ˆ -˜�Y‹ -˜ÛÛ[X[™‚\Ú^™WÝÚ^™HH ‚\™Y™—ÝÜÈH ‚P\™Ó\Ý\™Û\Ý‚XÚ\ˆ\™Ø�Y–ÓPVÐT‘×ÓSˆ -È WHHˆŽÂ‚\×ÜÙ]Ù[�žH -œÙ]H]™[Ù]È -ÈÝ\œ™[�ÜÙ]‚\×ÜØ]™[]™[ -œØ]™HHØ]™[]™[ -ÈÝ\œ™[�ÜÙ]‚Z[�ØYØÛÝ[�H ‚Z[�Ø]™YÜÙ[XÝÜØÜ™Y[ˆH ‚Z[�\×Ùš\œÝÜÙ[XÝH N‚‚\Ø]™[]™[[™›Ê -N‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÔÑSPÕ‚ˆÚ[Ø[ - -N‚‚KËÈ[š]X[^™H^Y\ˆÚ^™Y\œ˜^\Ë‚‚Y[�]H -™^[\VÜÙ] O›X^^Y\œ×N‚Z[�™XYVÜÙ] O›X^^Y\œ×N‚‚KËÈ[š]X[^™H‚Y›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚^‚BY^[\VÚWHH•S‚B\™XYVÚWHH ‚_B‚‚KËÈ[ÝÈÙ[XÝÈ ØIÈ\ÈHš\œÝÚ\ˆÙˆ[ÝÜÙ[XÝ ‚KËÈYˆ\™IÜÈ ØIÈ[ˆ\™H\È[ÝÜÙ[XÝ ‚‚ZYˆ -X[ÝÜÙ[XÝØ\™Ü‚B_ -[ÝÜÙ[XÝØ\™ÜÖÌHOH ØI‚BBI‰ˆ[ÝÜÙ[XÝØ\™ÜÖÌHOH ÐIÊJB‚^‚B\™\Ù]Ü^XX›WÛ\Ý - JN‚_B‚‚KËÈ™\Ù]Y[[ÜžH›Üˆ^Y\ˆ\œ˜^K‚‚[Y[\Ù] -^Y\‹ Ú^™[ÙŠ -œ^Y\ŠH -ˆPVÔVQT”ÊN‚‚KËÈØYØ[YHÙ[XÝY[™HØ]™HØ[YH]˜Z[X›O‚ZYˆ -\ÙTØ]™YØ[YH ‰ˆØ]™JB‚^‚BZYˆ -Ø]™KOœÙ[XÝ›YÊB‚B^‚BB[ØYÜÙ[XÝÜØÜ™Y[—Ú[™›ÊØ]™JN‚BB[ØYÜ^XX›WÛ\Ý -‚BBBYÙ]ÜØ]™YØ[ÝÜÙ[XÝØ\™Ý[Y[�ÊÝ\œ™[�ÜÙ] -B‚BBJN‚BB\Ø]™YÜÙ[XÝÜØÜ™Y[ˆH N‚B_B‚_B‚‚KËÈX\šÈš\Ü^YYˆ›Üˆ[^Y\œË‚‚Y›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚^‚B\^Y\–ÚWKš\Ü^YYH^Y\œÖÚWN‚_B‚‚Y›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚^‚BZYˆ -Ø]™[]™[ØÝ\œ™[�ÜÙ]Kœ]™\ÖÚWHˆ -B‚B^‚BBZ\×Ùš\œÝÜÙ[XÝH ‚BBXœ™XZ΂B_B‚_B‚‚KËÈØY[™\HÙ[XÝ[Ûˆ^š[K‚‚ZYˆ -š[[˜[YH ‰ˆš[[˜[YVÌJB‚^‚BZYˆ -�Y™™\—ÜZÙš[Jš[[˜[YK ˜�Y‹ œÚ^™JHOH JB‚B^‚BBX›Ü”Ú]ÝÛŠ K‘˜Z[YÈØY^Y\ˆÙ[XÝš[H É\ÉÈ‹š[[˜[YJN‚B_B‚B]Ú[H -ÜÈÚ^™JB‚B^‚BBT\œÙP\™ÜÊ ˜\™Û\Ý �Yˆ -ÈÜË\™Ø�YŠN‚BBXÛÛ[X[™HÑUÐT‘Ê -N‚BBZYˆ -ÛÛ[X[™ ‰ˆÛÛ[X[™ÌJB‚BB^‚BBBZYˆ -ÝšXÛ\ -ÛÛ[X[™ ›]\ÚXÈŠHOH -B‚BBB^‚BBBB[]\ÚXÊÑUÐT‘Ê JKÑUÒS•ÐT‘Ê ŠK]Û -ÑUÐT‘Ê ÊJJN‚BBBBKËÈÐU‘B‚BBBB[][\ݘØ]Ü -Ø]™KOœÙ[XÝ]\ÚXËÛÛ[X[™ ÑUÐT‘Ê JKÑUÐT‘Ê ŠKÑUÐT‘Ê ÊK•S -N‚BBB_B‚BBBY[ÙHYˆ -ÝšXÛ\ -ÛÛ[X[™ ˜[ÝÜÙ[XÝŠHOH -B‚BBB^‚BBBB[ØYÜ^XX›WÛ\Ý -�Yˆ -ÈÜÊN‚BBBB\Ù]ÜØ]™YØ[ÝÜÙ[XÝØ\™Ý[Y[�Ê‚BBBBBXÝ\œ™[�ÜÙ] ‚BBBBBX[ÝÜÙ[XÝØ\™Ü‚BBBBJN‚BBB_B‚BBBY[ÙHYˆ -ÝšXÛ\ -ÛÛ[X[™ ˜˜XÚÙÜ›Ý[™ŠHOH -B‚BBB^‚BBBB[ØYؘXÚÙÜ›Ý[™ -ÑUÐT‘Ê JJN‚BBBBKËÈÐU‘B‚BBBB[][\ݘØ]Ü -Ø]™KOœÙ[XݘXÚÙÜ›Ý[™ ÛÛ[X[™ ÑUÐT‘Ê JK•S -N‚BBB_B‚BBBY[ÙHYˆ -ÝšXÛ\ -ÛÛ[X[™ ›ØYŠHOH -B‚BBB^‚BBBB][\[Ù[Hš[™[Ù[ -ÑUÐT‘Ê JJN‚BBBBZYˆ -][\[Ù[ -B‚BBBB^‚BBBBB[ØYØØXÚYÛ[Ù[ -ÑUÐT‘Ê JKš[[˜[YKÑUÒS•ÐT‘Ê ŠJN‚BBBB_B‚BBBBY[ÙB‚BBBB^‚BBBBB]\]WÛ[Ù[ÛØY›YÊ[\[Ù[ ÑUÒS•ÐT‘Ê ŠJN‚BBBB_B‚BBBBKËÈÐU‘B‚BBBBZYˆ -ØYØÛÝ[�PVÔÑSPÕÓÐQÊB‚BBBB^‚BBBBB[][\ݘØ]Ü -Ø]™KOœÙ[XÝØYÛØYØÛÝ[�KÛÛ[X[™ ÑUÐT‘Ê JKÑUÐT‘Ê ŠK•S -N‚BBBBB[ØYØÛÝ[� -Ê΂BBBB_B‚BBB_B‚BBBY[ÙHYˆ -ÛÛ[X[™ ‰ˆÛÛ[X[™ÌJB‚BBB^‚BBBB\š[�Š�ÛÛ[X[™ É\ÉÈ\È›Ý[™\œÝÛÙ[ˆš[H É\É׈‹ÛÛ[X[™ š[[˜[YJN‚BBB_B‚BB_B‚‚BB\ÜÈ -ÏHÙ]™]Ó[™TÝ\� -�Yˆ -ÈÜÊN‚B_B‚B\Ø]™KOœÙ[XÝØYÛÝ[�HØYØÛÝ[�È ËÈÐU‘H�[X™\ˆÙˆÐQÛÛ[X[™‚B\Ø]™KOœÙ[XÝ›YÈH N‚‚BZYˆ -�YˆOH•S -B‚B^‚BBYœ™YJ�YŠN‚BBX�YˆH•S‚B_B‚_B‚Y[ÙH ËÈÚ]Ý]Ù[XÝ �‚^‚BZYˆ -\×Ùš\œÝÜÙ[XÝ -\ÚÚ\Ù[XÝÌVÌH ‰ˆ\Ù] O››ÜÙ[XÝ -JH ËÈ›ÈÙ[XÝ\ÈÚÚ\Ù[XÝÚ]Ý]˜[Y\‚B^‚BBYY˜][Ù[XÝH NÈ ËțܛX[Ù[XÝ܈ÚÚ\Ù[XÝ Û›ÜÙ[XÝÈ HOH›Ü›X[Ù[XÝ‚B_B‚‚BZYˆ -[›ÜÚ\™JB‚B^‚BBXÜ™Y]ÈHÓÓ•S•QT΂B_B‚BY[ÙH›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚B^‚BBZYˆ -^Y\œÖÚWJB‚BB^‚BBB\^Y\–ÚWK˜Ü™Y]ÈHÓÓ•S•QT΂BB_B‚B_B‚‚BZYˆ -ÚÚ\Ù[XÝÌVÌHÙ] O››ÜÙ[XÝ -B‚B^‚BBY›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚BB^‚BBBZYˆ -\^Y\œÖÚWJB‚BBB^‚BBBBXÛÛ�[�YN‚BBB_B‚BBB\Ý›˜ÜJ^Y\–ÚWK›˜[YKÚÚ\Ù[XÝÚWKPVÓ�SQWÓSŠN‚‚BBBZYˆ -Y˜][Ù[XÝ -B‚BBB^‚BBBB\^Y\–ÚWK›]™\ÈHVQT—ÓU‘T΂BBBBZYˆ -JÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÐÔ‘QU×ÐPÕU‘JJB‚BBBB^‚BBBBBZYˆ -›ÜÚ\™JB‚BBBBB^‚BBBBBBKK\^Y\–ÚWK˜Ü™Y]΂BBBBB_B‚BBBBBY[ÙB‚BBBBB^‚BBBBBBKKXÜ™Y]΂BBBBB_B‚BBBB_B‚BBB_B‚BBBY[ÙB‚BBB^‚BBBB\^Y\–ÚWK›]™\ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]Kœ]™\ÖÚWN‚BBBB\^Y\–ÚWKœØÛÜ™HHØ]™[]™[ØÝ\œ™[�ÜÙ]KœØÛÜ™\ÖÚWN‚BBBBZYˆ -›ÜÚ\™JH^Y\–ÚWK˜Ü™Y]ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]KœÜ™Y]ÖÚWN‚BBBBY[ÙHÜ™Y]ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]K˜Ü™Y]΂BBB_B‚BB_B‚‚BB\ØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÔÑSPÕ‚‚BB\™]\›ˆ N‚B_B‚‚BZYˆ -\Ø]™YÜÙ[XÝÜØÜ™Y[ŠB‚B^‚BBZYˆ -[›ØÚØ™È ‰ˆ›Û�\ÊB‚BB^‚BBBKËÈ™]È[\›˜]]™H˜XÚÙÜ›Ý[™] ‚‚BBBZYˆ -Ý\ÝšÙÜ™ÈOH•S -B‚BBB^‚BBBB\ݘÜJÝš[™ËÝ\ÝšÙÜ™ÊN‚BBBB\ݘØ] -Ýš[™Ë�[›ØÚؙȊN‚BBBB[ØYؘXÚÙÜ›Ý[™ -Ýš[™ÊN‚BBB_B‚BBBY[ÙB‚BBB^‚BBBB[ØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÝ[›ØÚؙȊN‚BBB_B‚BB_B‚BBY[ÙB‚BB^‚BBBKËÈ™]È[\›˜]]™H˜XÚÙÜ›Ý[™] ‚‚BBBZYˆ -Ý\ÝšÙÜ™ÈOH•S -B‚BBB^‚BBBB\ݘÜJÝš[™ËÝ\ÝšÙÜ™ÊN‚BBBB\ݘØ] -Ýš[™ËœÙ[XÝŠN‚BBBB[ØYؘXÚÙÜ›Ý[™ -Ýš[™ÊN‚BBB_B‚BBBY[ÙB‚BBB^‚BBBB[ØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÜÙ[XÝŠN‚BBB_B‚BB_B‚BBZYˆ -[]\ÚXÊ™]KÛ]\ÚXËÛY[�H‹ K -JB‚BB^‚BBB[]\ÚXÊ™]KÛ]\ÚXËÜ™[Z^‹ K -N‚BB_B‚B_B‚_B‚‚Y›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚^‚BZYˆ -^Y\œÖÚWJB‚B^‚BBY^[\VÚWHHÜ]Û™^[\JJN‚BB\^Y\–ÚWKœ^ZÙ^\ÈH ‚BBZYˆ -Y˜][Ù[XÝ -B‚BB^‚BBB\^Y\–ÚWK›]™\ÈHVQT—ÓU‘T΂BBBZYˆ -JÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÐÔ‘QU×ÐPÕU‘JJB‚BBB^‚BBBBZYˆ -›ÜÚ\™JB‚BBBB^‚BBBBBKK\^Y\–ÚWK˜Ü™Y]΂BBBB_B‚BBBBY[ÙB‚BBBB^‚BBBBBKKXÜ™Y]΂BBBB_B‚BBB_B‚BB_B‚BBY[ÙB‚BB^‚BBB\^Y\–ÚWK›]™\ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]Kœ]™\ÖÚWN‚BBB\^Y\–ÚWKœØÛÜ™HHØ]™[]™[ØÝ\œ™[�ÜÙ]KœØÛÜ™\ÖÚWN‚BBBZYˆ -›ÜÚ\™JH^Y\–ÚWK˜Ü™Y]ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]KœÜ™Y]ÖÚWN‚BBBY[ÙHÜ™Y]ÈHØ]™[]™[ØÝ\œ™[�ÜÙ]K˜Ü™Y]΂BB_B‚B_B‚_B‚‚WÝ[YHH ‚‚KËÈÝ^H[ˆÙ[XÝ[Ûˆ[�[\ØØ\H܈^] ‚‚KËÈ‚KËÈ^]H[^Y\œÈ™XYH -Ù[XÝY -H[™^][^H^\™Y ‚‚KËÈ\ØØ\HH\ØØ\HÙ^H™\ÜÙY ‚‚]Ú[H -J^]\ØØ\JJB‚^‚B\^Y\œ×Ø�\ÞHH ‚B\^Y\œ×Ü™XYHH ‚‚BKËÈÛÜ›ÝYÚ^Y\œË‚‚BY›Üˆ -HH ÈHÙ] O›X^^Y\œÎÈJÊÊB‚B^‚BBKËÈÝ\œ™[�^Y\ˆ[™^›ÝY]Ù[XÝY‚BBZYˆ -\™XYVÚWJB‚BB^‚BBBB‚BBBKËÈ\È\ÈÚ\™HÙH™\Ù[�^Y\ˆÙ[XÝ[ۜˈHÙÚXÈ\Èۙ‚BBBKËÈ[™H]HY\ÜÞKÛÈ�XÚÛH\H˜\ÚXØ[KÙHØ[�ÈÜ]Û‚‚BBBKËÈ[ˆ^[\H[�]HÈÙ]Ý\�Y [™]^[\H[�]B‚BBBKËÈ\ÈÚ]^Y\ˆÙY\ÈÛˆHÙ[XÝ[ÛˆØÜ™Y[‹ˆ[ˆÙHÝÚ]Ú‚BBBKËÈ]È[Ù[ ØÛÛÜ‹Ø[š[X][Ûˆ˜\ÙYÛˆHÚ]X][Ûˆ[™^Y\‚‚BBBKËÈ[œ] ‚‚BBBKË‚BBBKËÈ KˆYˆ[ˆ^[\H[�]H^\ÝÈ[™\È^Z[™ÈH˜[œÚ][Ûˆ‚BBBKËÈ[ˆÙK‹‹‚‚BBBKË‚BBBKËÈJHÈ›Ý[™ÈYˆH[š[X][Ûˆ\ۉݚ[š\ÚY ‚‚BBBKË‚BBBKËÈŠHYˆ]TÈš[š\ÚY ‹‹‚‚BBBKË‚BBBKËÈ KH KˆYˆH[š[X][Ûˆ\ÈS’WÔÑSPÕS‹[ˆ^HS’WÔÑSPÕ ‚‚BBBKËÈ‚BBBKËÈ KH ‹ˆYˆ[šX[]Ûˆ\ÈS’WÔÑSPÕÕU [ˆÙHÝÚ]ÚÈ™]È‚BBBKËÈ[Ù[ -Yˆ]˜Z[X›JK‚‚BBBKË‚BBBKËÈ ‹ˆYˆ^Y\ˆ\Û‰Ý^YYY] \ÈÛÛYHÜ™Y]È܈‚BBBKËÈØ[ˆ˜]Èœ›ÛHÜ™Y]ÛÛ[™™\ÜÙY[žHXÝ[Ûˆ�]Û‹‚BBBKËÈ[ˆÙIÛX[Ú]Z\ˆÜ™Y]ÛÛ[™Ü]ÛˆB‚BBBKËÈš\œÝ^[\H -Ù[XÝX›H[Ù[™]šY]ÊKˆ]š[™È[ˆ‚BBBKËÈ^[\HÜ]Û™Y[ÛÈ[È\ÈH^Y\ˆ\ÈÛÛ\]Y‚BBBKËÈ\ÈÝ\ [™ÛÈ] ÜÈÒÈÈ�[ˆXÝ[ۜȜ›ÛH[žHÙˆ‚BBBKËÈHÝ\œË‚‚BBBKË‚BBBKËÈ ËˆYˆH^Y\ˆ™\ÜÙYY�܈šYÚ[œÝXY[™\™IÜ‚BBBKËÈ[ˆ^[\HÜ]Û™Y [ˆÙHš[™H™]š[Ý\ËÛ™^‚BBBKËÈÚ\˜XÝ\ˆ[ˆ[™K[™™XÛÜ™]ÈH˜\šXX›Kˆ[ˆÙB‚BBBKËÈÙYHYˆ^[\H\ÈS’WÔÑSPÕS‹ˆYˆ]Ù\Û‰ÝÙHÝÚ]Ú‚BBBKËÈÈ™]È[Ù[ ˆYˆ]Ù\Ë^HS’WÔÑSPÕ ‚‚BBBKË‚BBBKËÈ ˆYˆH^Y\ˆ™\ÜÙ\È\܈ÝÛ‹ÙH]™H[ˆ^[\H‚BBBKËÈÜ]Û™Y[™ÛÛÝ\œÙ[XÝ\È[˜X›Y [ˆÞXÛHÈB‚BBBKËÈ[Ù[ ÜÈ™]š[Ý\ËÛ™^ÛÛ܈Ù]ÚÚXÙK‚‚BBBKË‚BBBKËÈ KˆYˆH^Y\ˆ™\ÜÙ\È[žHXÝ[Ûˆ�]Ûˆ[™ÙH]™B‚BBBKËÈ[ˆ^[\HÜ]Û™Y [ˆÙHX\šÈH^Y\‰ÜÈ™XYH[^H‚BBBKËÈ›YË[™Ý[[YKˆÙYHH\™[�ÙÚXÈ›ØÚț܈‚BBBKËÈÙ[XÝ[Ûˆ[^H ˆ^]]Z[ˈ\È\ÈH^Y\‚‚BBBKËÈXZÚ[™ÈZ\ˆÙ[XÝ[ÛˆÚÚXÙK‚‚‚BBBKËÈ^[\H^\ÝÈ[™Ù[Xݘ[œÚ][Ûˆ[š[X][Û�‚BBBZYˆ -^[\VÚWH‚BBBBI‰ˆ -^[\VÚWKO˜[š[[�[HOHS’WÔÑSPÕSˆ^[\VÚWKO˜[š[[�[HOHS’WÔÑSPÕÕU -JB‚BBB^‚BBBBKËÈYˆÝ[[š[X][™È[ˆÈ›Ý[™Ëˆ]H˜[œÚ][Ûˆš[š\Ú ‚‚BBBBZYˆ -^[\VÚWKO˜[š[X][™ÊB‚BBBB^‚BBBB_B‚BBBBY[ÙB‚BBBB^‚BBBBBKËȘ[œÚ][ÛˆÈÙ[XÝ[š[X][Û‹‚‚BBBBBZYˆ -^[\VÚWKO˜[š[[�[HOHS’WÔÑSPÕSŠB‚BBBBB^‚BBBBBBY[�ÜÙ]Ø[š[J^[\VÚWKS’WÔÑSPÕ  -N‚BBBBB_B‚‚BBBBBKËȘ[œÚ][Ûˆœ›ÛHÙ[XÝ -^Y\ˆÙ[XÝY[›Ý\ˆ[Ù[ [™B‚BBBBBKËÈÙ[XÝÝ]˜[œÚ][Ûˆ\È›ÝÈš[š\ÚY -Kˆ™\X]ÙˆY� ÜšYÚÙ^H‚BBBBBKËÈÙÚXÈ™[ÝÈ[™›Ø˜X›H™YYÈÛÛœÛÛY][Û‹‚‚BBBBBZYˆ -^[\VÚWKO˜[š[[�[HOHS’WÔÑSPÕÕU ‰ˆ[Ù[Û™]ÖÚWJB‚BBBBB^‚BBBBBBKËÈ\H™]È[Ù[ ‚‚BBBBBBY[�ÜÙ]Û[Ù[ -^[\VÚWK[Ù[Û™]ÖÚWKO›˜[YK -N‚BBBBBB[[Ù[Û™]ÖÚWHH•S‚‚BBBBBBKËÈÛÜH^[\H[Ù[˜[YHÈ^Y\ˆ˜[YH˜\šXX›K‚‚BBBBBB\ݘÜJ^Y\–ÚWK›˜[YK^[\VÚWKO›[Ù[ O›˜[YJN‚‚BBBBBBKËÈYˆÛÛÜœÙ[XÝ\È[˜X›Y[™›ÜØ[YH ˆ\È[˜X›Y ÚÚ\‚BBBBBBKËÈÝ\�]™^]˜ZX[›HÛÛ܈ÞXÛKˆÝ\�Ú\ÙH�\ÝÝ\�‚BBBBBBKËÈÚ]Y˜][ÛÛ܈Ù] - -K‚‚BBBBBBZYˆ -ÛÛÝ\œÙ[XÝ ‰ˆ -Ù] O››ÜØ[YH ˆ ŠJB‚BBBBBB^‚BBBBBBB\^Y\–ÚWK˜ÛÛÝ\›X\H™^ÛÛÝ\›X\Š^[\VÚWKO›[Ù[  LKJN‚BBBBBB_B‚BBBBBBY[ÙB‚BBBBBB^‚BBBBBBB\^Y\–ÚWK˜ÛÛÝ\›X\H ‚BBBBBB_B‚‚BBBBBBKËÈ\HÛÛ܈Ù] ‚‚BBBBBBY[�ÜÙ]ØÛÛÝ\›X\ -^[\VÚWK^Y\–ÚWK˜ÛÛÝ\›X\ -N‚BBBBB_B‚BBBB_B‚BBB_B‚BBBY[ÙHYˆ -\^Y\–ÚWKš\Ü^YY‚BBBBI‰ˆ -›ÜÚ\™HÜ™Y]Ȉ -B‚BBBBI‰ˆ -^Y\–ÚWK›™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠJB‚BBB^‚‚BBBBKËÈ›ÝÈ\È^Y\ˆ\È^YY ‚‚BBBB\^Y\œÖÚWHH^Y\–ÚWKš\Ü^YYH N‚BBBBKËÜš[�ЉY Y Yˆ‹K^Y\–ÚWK›]™\Ë[[YYX]VÚWJN‚‚BBBBKËÈ›ÜÚ\™HYX[œÈXXÚ^Y\ˆ\ÈZ\ˆÝۈܙY]ÛÛ ‚‚BBBBZYˆ -›ÜÚ\™JB‚BBBB^‚BBBBB\^Y\–ÚWK˜Ü™Y]ÈHÓÓ•S•QT΂BBBB_B‚‚BBBBKËÈÜ™Y]ÈÚX]H[™š[š]HÜ™Y]ˈYˆ] ÜÈ›Ý[˜X›Y ‚BBBBKËÈ[ˆYXÝHÜ™Y] ‚‚BBBBZYˆ -JÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÐÔ‘QU×ÐPÕU‘JJB‚BBBB^‚BBBBBZYˆ -›ÜÚ\™JB‚BBBBB^‚BBBBBBKK\^Y\–ÚWK˜Ü™Y]΂BBBBB_B‚BBBBBY[ÙB‚BBBBB^‚BBBBBBKKXÜ™Y]΂BBBBB_B‚BBBB_B‚‚BBBBKËÈÚ]™H^Y\ˆY˜][�[X™\ˆÙˆ]™\ËÜ]Û‚‚BBBBKËÈ^[\H[Ù[[™Ø[˜Ù[HÙ^H›YË‚‚BBBB\^Y\–ÚWK›]™\ÈHVQT—ÓU‘T΂BBBBY^[\VÚWHHÜ]Û™^[\JJN‚BBBB\^Y\–ÚWKœ^ZÙ^\ÈH ‚‚BBBBKËÈ^HÛÝ[™Y™™XÝ ‚‚BBBBZYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\�H -B‚BBBB^‚BBBBB\ÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -N‚BBBB_B‚BBB_B‚BBBY[ÙHYˆ - -^Y\–ÚWK›™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠH ‰ˆ^[\VÚWJH ËÒܘ]\È - KL KLŒJH[Ý™YH˜[žX�]ÛˆˆÛÙHÈ™Y›Ü™HÙˆH›Y� ÜšYÚˆÛÙHÈš^H�YÈ]XZÙ\È›ÈÚ\˜XÝ\ˆÚÜÙ[ˆÚ[ˆ›Ý\™H™\ÜÙYÙÙ]\‚‚BBB^‚BBBBZYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -B‚BBBB^‚BBBBB\ÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -N‚BBBB_B‚BBBBKËÈX^H[ÝHXÚÙYYHB‚BBBBZYˆ -˜[Y[š[J^[\VÚWKS’WÔPÒÊJB‚BBBB^‚BBBBBY[�ÜÙ]Ø[š[J^[\VÚWKS’WÔPÒË -N‚BBBB_B‚BBBBY^[\VÚWKOœÝ[[YHHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ŽÂ‚BBBB\™XYVÚWHH N‚BBB_B‚BBBY[ÙHYˆ -^Y\–ÚWK›™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -H ‰ˆ^[\VÚWJB‚BBB^‚BBBBKËÈÚ]™H^Y\ˆH™YY˜XÚÈÛÝ[™ ‚‚BBBBZYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\�H -B‚BBBB^‚BBBBB\ÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -N‚BBBB_B‚‚BBBBKËÈÙ][Ù[[ˆ\ÙHšYÚ›ÝË‚‚BBBBKËÈ] ÜÈÙ]H™]È[Ù[ ˆY�Ù^HH™]š[Ý\È[Ù[‚BBBBKËÈ[ˆÞXÛKˆšYÚÙ^HH™^ ‚‚BBBBZYˆ - -^Y\–ÚWK›™]ÚÙ^\È ˆ“Q×ÓSÕ‘SQ• -JB‚BBBB^‚BBBBB[[Ù[Û™]ÖÚWHH™]œ^Y\›[Ù[Š^[\VÚWKO›[Ù[ JN‚BBBB_B‚BBBBY[ÙB‚BBBB^‚BBBBB[[Ù[Û™]ÖÚWHH™^^Y\›[Ù[Š^[\VÚWKO›[Ù[ JN‚BBBB_B‚‚BBBBKËÈÈÙH]™HHÙ[XÝÝ]˜[œÚ][Û�ÈYˆÛÈ^H]\™Kˆ‚BBBBKËÈÝ\�Ú\ÙHÝÚ]ÚÈ™]È[Ù[ ˆ‚BBBBZYˆ -˜[Y[š[J^[\VÚWKS’WÔÑSPÕÕU -JB‚BBBB^ÂBBBBBB‚BBBBBY[�ÜÙ]Ø[š[J^[\VÚWKS’WÔÑSPÕÕU  -NÂBBBB‚BBBB_B‚BBBBY[ÙB‚BBBB^‚BBBBBKËÈ\H™]È[Ù[ ‚‚BBBBBY[�ÜÙ]Û[Ù[ -^[\VÚWK[Ù[Û™]ÖÚWKO›˜[YK -N‚BBBBB[[Ù[Û™]ÖÚWHH•S‚‚BBBBBKËÈÛÜH^[\H[Ù[˜[YHÈ^Y\ˆ˜[YH˜\šXX›K‚‚BBBBB\ݘÜJ^Y\–ÚWK›˜[YK^[\VÚWKO›[Ù[ O›˜[YJN‚‚BBBBBKËÈYˆÛÛÜœÙ[XÝ\È[˜X›Y[™›ÜØ[YH ˆ\È[˜X›Y ÚÚ\‚BBBBBKËÈÝ\�]™^]˜ZX[›HÛÛ܈ÞXÛKˆÝ\�Ú\ÙH�\ÝÝ\�‚BBBBBKËÈÚ]Y˜][ÛÛ܈Ù] - -K‚‚BBBBBZYˆ -ÛÛÝ\œÙ[XÝ ‰ˆ -Ù] O››ÜØ[YH ˆ ŠJB‚BBBBB^‚BBBBBB\^Y\–ÚWK˜ÛÛÝ\›X\H™^ÛÛÝ\›X\Š^[\VÚWKO›[Ù[  LKJN‚BBBBB_B‚BBBBBY[ÙB‚BBBBB^‚BBBBBB\^Y\–ÚWK˜ÛÛÝ\›X\H ‚BBBBB_B‚‚BBBBBKËÈ\HÛÛ܈Ù] ‚‚BBBBBY[�ÜÙ]ØÛÛÝ\›X\ -^[\VÚWK^Y\–ÚWK˜ÛÛÝ\›X\ -N‚BBBB_BBBBBB‚BBB_B‚BBBY[ÙHYˆ -^Y\–ÚWK›™]ÚÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓŠH ‰ˆÛÛÝ\œÙ[XÝ ‰ˆ^[\VÚWJB‚BBB^‚BBBB\^Y\–ÚWK˜ÛÛÝ\›X\H - -^Y\–ÚWK›™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -HÈ™^ÛÛÝ\›X\ˆˆ™]˜ÛÛÝ\›X\ŠJ^[\VÚWKO›[Ù[ ^Y\–ÚWK˜ÛÛÝ\›X\ JN‚BBBBY[�ÜÙ]ØÛÛÝ\›X\ -^[\VÚWK^Y\–ÚWK˜ÛÛÝ\›X\ -N‚BBB_B‚BB_B‚BBY[ÙHYˆ -™XYVÚWHOH JB‚BB^‚BBBZYˆ - - -]˜[Y[š[J^[\VÚWKS’WÔPÒÊH^[\VÚWKO›[Ù[]K˜[š[X][Û–ÐS’WÔPÒ×KO›ÛÜ ›[ÙJH ‰ˆÝ[YHˆ^[\VÚWKOœÝ[[YJHY^[\VÚWKO˜[š[X][™ÊB‚BBB^‚BBBB\™XYVÚWHH ŽÂ‚BBBBY^][^HHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY‚BBB_B‚BB_B‚BBY[ÙHYˆ -™XYVÚWHOH ŠB‚BB^‚BBBY›Û�Üš[�ŠÛY[�VÚWVÌ—KÛY[�VÚWVÌ×K  Š”™XYHHŠJN‚BB_B‚‚BBZYˆ -^[\VÚWHOH•S -B‚BB^‚BBB\^Y\œ×Ø�\ÞJÊ΂BB_B‚BBZYˆ -™XYVÚWHOH ŠB‚BB^‚BBB\^Y\œ×Ü™XYJÊ΂BB_B‚B_B‚‚BZYˆ -^Y\œ×Ø�\ÞH ‰ˆ^Y\œ×Ø�\ÞHOH^Y\œ×Ü™XYH ‰ˆ^][^H ‰ˆÝ[YHˆ^][^JB‚B^‚BBY^]H N‚B_B‚B]\]J  -N‚‚BZYˆ -›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÈ\ØØ\WÙ›YÈOH LJH ËÒܘ]\È - Œ L LŒJHYYH™]È™\ØØ\Hˆ›YÈ[ˆHÙ[XÝØÜ™Y[ˆžH\Ú[™ÈH™ÛÝÛXZ[›Y[�Hˆ�[˜Ý[Ûˆ[™H›YÈŒLH‚‚B^‚BBY\ØØ\HH Nˆ\ØØ\WÙ›YÈH ‚B_B‚_B‚‚KËÈ›ÈÛ™Ù\ˆ]HÙ[XÝØÜ™Y[‚‚ZÚ[Ø[ - -N‚\ÛÝ[™ØÛÜÙWÛ]\ÚXÊ -N‚\ØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÔÑSPÕ‚‚\™]\›ˆ -Y\ØØ\JNŸB‚�›ÚY^YØ[YJ[� -œ^Y\œË[œÚYÛ™YÚXÚÜÙ] [�\ÙTØ]™YØ[YJBžÂˆ[�NˆÝ\œ™[�Û]™[H ˆÝ\œ™[�ÜÝYÙHH NˆÝ\œ™[�ÜÙ]HÚXÚÜÙ]ˆ×ÜÙ]Ù[�žH -œÙ]H]™[Ù]È -ÈÝ\œ™[�ÜÙ]ˆ×ÜØ]™[]™[ -œØ]™HHØ]™[]™[ -ÈÝ\œ™[�ÜÙ]ˆ×Û]™[Ù[�žH -›N‚ˆ\ÙTØ]™HH ˆ\ÙTÙ]H LN‚ˆYŠÚXÚÜÙ]�H�[WÙY™šXÝ[Y\ÊBˆˆ™]\›ŽÂˆBˆ ËțܔÚ]ÝÛŠ K’[YØ[Ù]ÚÜÙ[Žˆ[™^ ZH -\™H\™HÛ›H ZHÙ]ÊHH‹ÚXÚÜÙ] �[WÙY™šXÝ[Y\ÊN‚ˆ[Ý×ÜÙXÜ™]ØÚ\œÈHÙ] OšY˜ÛÛ\]NˆVQT—ÓU‘TÈHÙ] O›]™\Έ]\ÚXÛÝ™\›\HÙ] O›]\ÚXÛÝ™\›\ˆ˜YHHÙ] O˜Ý\ݘYNˆÓÓ•S•QTÈHÙ] O˜Ü™Y]ΈXYÚX×Ý\HHÙ] O�\[\ˆYŠVQT—ÓU‘TÈOH -BˆˆVQT—ÓU‘TÈH ÎÈ ËÈY˜][ˆBˆYŠÓÓ•S•QTÈOH -BˆˆÓÓ•S•QTÈH NÈ ËÈY˜][ˆBˆYŠ˜YHOH -Bˆˆ˜YHH �È ËÈY˜][ˆBˆØ[Y\^Y\ˆHÙ] O››ÜØ[YN‚ˆYŠ\ÙTØ]™YØ[YHOH H ‰ˆØ]™KO™›YÊBˆˆY[\Ù] -^Y\‹ Ú^™[ÙŠ -œ^Y\ŠH -ˆ -NˆYŠ[ØYØÜš\š[J -JBˆˆš[�Š•Ø\›š[™Ë˜Z[YÈØYØÜš\Ø]™HWˆŠNˆBˆÝ\œ™[�Û]™[HØ]™KO›]™[ˆÝ\œ™[�ÜÝYÙHHØ]™KOœÝYÙNˆYŠØ]™KO™›YÈOH ŠH ËÈÛ‰ÝÚXÚÈ H܈ ™XÝX\ÙHYˆÙH\ÙHØ]™YØ[YHH›YÈ]\Ý™HŒˆˆ›ÜŠHH ÈHÙ] O›X^^Y\œÎÈJÊÊBˆˆ^Y\–ÚWK›]™\ÈHØ]™KOœ]™\ÖÚWNˆ^Y\–ÚWK˜Ü™Y]ÈHØ]™KOœÜ™Y]ÖÚWNˆ^Y\–ÚWKœØÛÜ™HHØ]™KOœØÛÜ™\ÖÚWNˆ^Y\–ÚWK˜ÛÛÝ\›X\HØ]™KOœÛÛÝ\›X\ÚWNˆ^Y\–ÚWK�ÙX\�[HHØ]™KOœÙX\�[VÚWNˆ^Y\–ÚWKœÜ]ÛšX[HØ]™KOœÜ]ÛšX[ÚWNˆ^Y\–ÚWKœÜ]Û›\HØ]™KOœÜ]Û›\ÚWNˆÝ›˜ÜJ^Y\–ÚWK›˜[YKØ]™KOœ˜[YVÚWKPVÓ�SQWÓSŠNˆBˆÜ™Y]ÈHØ]™KO˜Ü™Y]ΈBˆØYÜ^XX›WÛ\Ý -ˆÙ]ÜØ]™YØ[ÝÜÙ[XÝØ\™Ý[Y[�ÊÝ\œ™[�ÜÙ] -Bˆ -Nˆ ËÜ™\Ù]Ü^XX›WÛ\Ý - JNÈ ËÈY\È™XØ]\ÙH\™IÜÈ›ÈÙ[XÝØÜ™Y[‹[\ܘ\žHÛÛ][Û‚ˆB‚ˆ›ÜØ]™HH N‚ˆ ËÈš^Ø]™H[ˆ[™^\Ý[�]™[ˆYˆ -Ý\œ™[�Û]™[�HÙ] O›�[[]™[È -BˆˆÝ\œ™[�Û]™[HÙ] O›�[[]™[ËLNˆB‚ˆHHÙ] O›]™[Ü™\ˆ -ÈÝ\œ™[�Û]™[ˆÙ] O››ÜÙ[XÝHKO››ÜÙ[X݈Yˆ -Ø]™KOœÙ[XÝÚÚ\Ù[XÝÌJHHHØYÜÚÚ\Ù[XÝ -Ø]™KOœÙ[XÝÚÚ\Ù[XÝ Ù] -NÈ ËÈÐQÚÚ\Ù[X݈›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYŠKOœÚÚ\Ù[XÝÚWJBˆˆÝ˜ÜJÚÚ\Ù[XÝÚWKKOœÚÚ\Ù[XÝÚWJNˆBˆ[ÙBˆˆÚÚ\Ù[XÝÚWVÌHH ˆBˆB‚ˆYŠ -\ÙTØ]™YØ[YHOH H ‰ˆØ]™KO™›YÈOH ŠH\ÙTØ]™YØ[YHOH ˆÙ[XÝ^Y\Š^Y\œË•S \ÙTØ]™YØ[YJJH ËÈYˆØ]™H›YÈ\È ˆÛ‰ÝÙ[XÝ^Y\‚ˆˆÚ[JÝ\œ™[�Û]™[Ù] O›�[[]™[ÊBˆˆYŠœ˜[˜ÚÛ˜[YVÌJH ËÈœ˜[˜ÚÚXÚÚ[™Âˆˆ ËØÝ\œ™[�ÜÝYÙHH NÈ ËÚ�[\ �[\ ‹‹ˆ\š\ÈÙHۉݙYYÈ™\Ù]] [Ù\œÈÚÝ[ZÙHØ\™HÙˆ] ‚ˆ›ÜŠHH ÈHÙ] O›�[[]™[ÎÈJÊÊBˆˆYŠÙ] O›]™[Ü™\–ÚWK˜œ˜[˜Ú˜[YH ‰ˆÝšXÛ\ -Ù] O›]™[Ü™\–ÚWK˜œ˜[˜Ú˜[YKœ˜[˜ÚÛ˜[YJHOH -BˆˆÝ\œ™[�Û]™[HNˆœ™XZΈBˆ ËÚYŠ]™[Ü™\–ÝÚXÚÜÙ]VÚWKO™ÛÛ™^OLJH -ÊØÝ\œ™[�ÜÝYÙNÈÖ ˆÛÛ[Y[�Y\È[™HÝ] ˆÙY[\ÈÈ™HØ]\ÙHÙˆ[˜XÝ\˜]HÝYÙHÈÛÛ\]HY\ÜØYÙK‚ˆBˆœ˜[˜ÚÛ˜[YVÌHH ËËÈÛX\ˆ\ÛÈÙHÛÛ‰ÝÝXÚÈ\™BˆBˆHHÙ] O›]™[Ü™\ˆ -ÈÝ\œ™[�Û]™[ˆVQT—ÓRS—ÖˆHKOž—ØÛÛÜ™ÖÌNˆVQT—ÓPVÖˆHKOž—ØÛÛÜ™ÖÌWNˆ‘ÒRQÒHKOž—ØÛÛÜ™ÖÌ—N‚ˆYŠKO�\HOHWÕTWÐÕUÔÐÑS‘JBˆˆ^\ØÙ[™JKO™š[[˜[YJNˆBˆ[ÙHYŠKO�\HOHWÕTWÔÑSPÕÔÐÔ‘QSŠBˆˆY[\Ù] -Ø]™KOœÙ[XÝÚÚ\Ù[XÝ Ú^™[ÙŠØ]™KOœÙ[XÝÚÚ\Ù[XÝ -JNÈ ËÈ‘TÑUÚÚ\Ù[X݈Ù] O››ÜÙ[XÝH ˆ›ÜŠHH ÈHÙ] O›X^^Y\œÎÈJÊÊH ËÈ™\Ù]ÚÚ\Ù[X݈ˆYŠKOœÚÚ\Ù[XÝÚWJBˆˆÚÚ\Ù[XÝÚWVÌHH ˆBˆBˆ›ÜŠHH ÈHÙ] O›X^^Y\œÈÈJÊÊBˆˆ^Y\œÖÚWHH -^Y\–ÚWK›]™\Ȉ -NˆBˆYŠÙ[XÝ^Y\Š^Y\œËKO™š[[˜[YK\ÙTØ]™YØ[YJHOH -Bˆˆœ™XZΈBˆBˆ[ÙHYŠKO�\HOHWÕTWÔÒÒTÔÑSPÕ -BˆˆÙ] O››ÜÙ[XÝHKO››ÜÙ[X݈›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYŠKOœÚÚ\Ù[XÝÚWJBˆˆÝ˜ÜJÚÚ\Ù[XÝÚWKKOœÚÚ\Ù[XÝÚWJNˆBˆ[ÙBˆˆÚÚ\Ù[XÝÚWVÌHH ˆBˆBˆØ]™WÜÚÚ\Ù[XÝ -Ø]™KOœÙ[XÝÚÚ\Ù[XÝ ÚÚ\Ù[XÝ -NÈ ËÈÈÐU‘H’SBˆÙ[XÝ^Y\Š^Y\œË•S \ÙTØ]™YØ[YJNÈ ËÈ™K\Ù[XÝH^Y\‚ˆBˆ[ÙHYŠ\^[]™[ -KO™š[[˜[YJJBˆˆ[�[ÜÛ]™\×Þ™\›ÈH ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆYˆ -^Y\–ÚWK›]™\ÈH -H -ÊØ[ÜÛ]™\×Þ™\›ÎˆBˆ[ÜÛ]™\×Þ™\›ÈH -[ÜÛ]™\×Þ™\›È�HPVÔVQT”ÊHÈ Hˆ ‚ˆ ËÚYŠ -^Y\–ÌK›]™\ÈH  ‰ˆ^Y\–ÌWK›]™\ÈH  ‰ˆ^Y\–Ì—K›]™\ÈH  ‰ˆ^Y\–Ì×K›]™\ÈH -H -BˆYŠ[ÜÛ]™\×Þ™\›ÊBˆˆYŠ -\Ù] O››ÜÚÝÙØ[Y[Ý™\ˆ ‰ˆJÛÝ×ÛXZ[›Y[�WÙ›YÉŒŠJH -BˆˆØ[Y[Ý™\Š -NˆBˆYŠ\Ù] O››ÜÚÝÚÙˆ ‰ˆJÛÝ×ÛXZ[›Y[�WÙ›YÉ� -JBˆˆ[˜[YJ JNˆBˆ›ÜŠHH ÈHÙ] O›X^^Y\œÎÈJÊÊBˆˆ^Y\–ÚWKš\Ü^YYH ˆ^Y\–ÚWK�ÙX\�[HH ˆBˆBˆœ™XZΈBˆYŠKO™ÛÛ™^OH JBˆˆYˆ -\Ù] O››ÜÚÝØÛÛ\]JHÚÝØÛÛ\]JÝ\œ™[�ÜÝYÙJNˆ›ÜŠHH ÈHÙ] O›X^^Y\œÎÈJÊÊBˆˆ^Y\–ÚWKœÜ]ÛšX[H ˆ^Y\–ÚWKœÜ]Û›\H ˆBˆ -ÊØÝ\œ™[�ÜÝYÙNˆØ]™KOœÝYÙHHÝ\œ™[�ÜÝYÙNˆBˆÝ\œ™[�Û]™[ -ÊΈHHÙ] O›]™[Ü™\ˆ -ÈÝ\œ™[�Û]™[ˆØ]™KO›]™[HÝ\œ™[�Û]™[ˆ ËÌŒ ËL‹L� ÛÛ™^H ‹[™Ø[YBˆYŠ -H H JKO™ÛÛ™^OH ŠBˆˆÝ\œ™[�Û]™[HÙ] O›�[[]™[ΈBˆYŠ\ÙTØ]™JBˆˆÛÝÈ[™Ø[YNÈ ËÜ]ZXÚÈ^]Ú]Ý]Ø]š[™Ë›ÜˆØÜš\ØYØ[YHÙÚXˆBˆKËÝÚ[B‚ˆYŠÝ\œ™[�Û]™[�HÙ] O›�[[]™[ÊBˆˆ Ê‚ˆܘ]\È - KLŒ � -H™\Ù]]™[ ÜÝYÙH˜[Y\ÈÚ[ˆHØ[YH\Èš[š\ÚY™Y›Ü™HØ]š[™È] ‚ˆ]Ú[]]ÛX]XØ[H\˜\ÙHHÝ\œ™[�Ø[YH›ÙÜ™\ÜÈ�]Û›HY�\ˆH[™[™Ë‚ˆÝÙ]™\‹]Ú[XZ[�Z[ˆH�[Y\רÛÛ\]Yˆ[�XÝ ‚ˆÚ]Ý]\Èš^H^Y\ˆØ[ˆ[˜Ü™X\ÙHH�[Y\רÛÛ\]Yˆ[™š[š][HžBˆ�\Ýš[š\Ú[™ÈHØ[YHÛ˜ÙH[™ØY[™ÈH[™[™È\™XÝHÝÈX[žH[Y\ÈHØ[� ‚ˆ -‹Âˆ›Û�\È -ÏHØ]™KO�[Y\רÛÛ\]Y -ÊΈÝ\œ™[�Û]™[H ˆÝ\œ™[�ÜÝYÙHH NˆØ]™KO›]™[HÝ\œ™[�Û]™[ˆØ]™KOœÝYÙHHÝ\œ™[�ÜÝYÙNˆØ]™QØ[YQš[J -NˆYŠ[›Ù˜Y[Ý] -Bˆˆ˜YWÛÝ] -  -NˆBˆYŠ\Ù] O››ÜÚÝÚÙŠBˆˆ[˜[YJ JNˆBˆBˆB‚™[™Ø[YN‚ˆ ËÈÛX\ˆÛؘ[ØÜš\˜\šX[�\݈œ˜[˜ÚÛ˜[YVÌHH ˆÛÝ[™ØÛÜÙWÛ]\ÚXÊ -NŸB‚š[�Y[�WÙY™šXÝ[J -BžÂˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�X^\Ü^HH Nˆ[�K‹ˆ ËÙ›Ø]ÛY\ˆH ˆ[�˜\ž ˜\žK˜\�˘\šˆ×Ù˜]ÛY]Ù˜]ÛY]ÙHZ[›Y]Ùˆ˜]ÛY]Ù ˜[HH“S‘ÓSÑWÐSN‚ˆ˜\žHšY[Û[Ù\Ëš™\È È Nˆ˜\žHHÛ[™^J  -H H ŽÂˆ˜\�ÈHšY[Û[Ù\Ëš™\È -ˆ È È Nˆ˜\šH H -ˆ -›Û�ZYÚ - -H -È JH -È ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—Ó‘U×ÑÐSQWÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆØYØ[YQš[J -N‚ˆÚ[J\]Z] -BˆˆYŠ�[WÙY™šXÝ[Y\Ȉ JBˆˆÛY[�]^J ‹ L‹ Š‘Ø[YH[ÙHŠJNˆH -Ù[XÝ܈ H -Ù[XÝ܈OH�[WÙY™šXÝ[Y\ÊJH ÈX^\Ü^H -ˆX^\Ü^Nˆ›ÜŠˆH HHÈHX^\Ü^H -È ‰ˆH�[WÙY™šXÝ[Y\ÎÈŠÊËJÊÊBˆˆYŠˆX^\Ü^JBˆˆ ËÈܘ]\È - L LŒ ŒJHYY˜[œÛ][Ûˆ™X]\™HÈHœÙ]ˆ˜[YBˆ ËÈ]Ú[Ù]HœÙ]ˆ˜[YH[ˆH›]™[Ë�ˆš[H[™˜[œÛ]H]ˆ ËÈ[ÝH™YYÈYH™]È›\ÙÚY Û\ÙÜ݈ˆ[œÝ[˜ÙH[ˆ[Ý\ˆ�˜[œÛ][Û‹�ˆš[H[™]ˆ ËÈHØ[YH˜[Y\È\È\ÙYžH[]™[Ù]ˆYŠ›Û�\È�H]™[Ù]ÖÚWKšY˜ÛÛ\]JBˆˆÛY[�]^J -Ù[XÝ܈OHJK‹ ‰\È‹Š]™[Ù]ÖÚWK›˜[YJJNˆBˆ[ÙBˆˆYŠ]™[Ù]ÖÚWKšY˜ÛÛ\]Hˆ JBˆˆÛY[�]^J -Ù[XÝ܈OHJK‹ Љ\È Hš[š\ÚØ[YH ZH[Y\ÈÈ[“ØÚÈŠKŠ]™[Ù]ÖÚWK›˜[YJK]™[Ù]ÖÚWKšY˜ÛÛ\]JNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OHJK‹ Љ\È Hš[š\ÚØ[YHÈ[“ØÚÈŠKŠ]™[Ù]ÖÚWK›˜[YJJNˆBˆBˆBˆ[ÙBˆˆœ™XZΈBˆBˆÛY[�]^J -Ù[XÝ܈OHJK ‹ Š�˜XÚÈŠJN‚ˆ ËÙ˜]ÈHØÜ›Û˜\‚ˆYŠ�[WÙY™šXÝ[Y\ȈX^\Ü^JBˆˆÜš]\WØYØ›Þ -˜\ž ˜\žK˜\�˘\š  ÛÛÜ—Ø›XÚË ™˜]ÛY]Ù -NÈ ËÛÝ]\˜›ÞˆÜš]\WØYÛ[™J˜\ž ˜\žK˜\ž -È ˜\žK KÛÛÜ—ÝÚ]K•S -NˆÜš]\WØYÛ[™J˜\ž ˜\žK˜\ž ˜\žH -Ș\š  KÛÛÜ—ÝÚ]K•S -NˆÜš]\WØYÛ[™J˜\ž -È ˜\žK˜\ž -È ˜\žH -Ș\š  KÛÛÜ—ÝÚ]K•S -NˆÜš]\WØYÛ[™J˜\ž ˜\žH -Ș\š ˜\ž -È ˜\žH -Ș\š  KÛÛÜ—ÝÚ]K•S -NˆÜš]\WØYØ›Þ -˜\ž -È K˜\žH -ÈÙ[XÝ܈ -ˆ -˜\š H ÊH È�[WÙY™šXÝ[Y\Ë Ë Ë ‹ÛÛÜ—ÝÚ]K•S -NÈ ËÜÛY\‚ˆBˆB‚ˆ\]J  -N‚ˆYŠ�[WÙY™šXÝ[Y\ÈOH JH ËÈÖ ˆ[ÙÈÚ]Û›HÛ™HÙ]Ú[]]ÈØY]Y™šXÝ[K‚ˆˆYŠÙ[XÝ܈OH�[WÙY™šXÝ[Y\ÊBˆˆ]Z]H NˆBˆ[ÙHYŠ›Û�\È�H]™[Ù]ÖÜÙ[XÝÜ—KšY˜ÛÛ\]JBˆˆØ]™\ÛÝHÙ[XÝ܎ˆÝ›˜ÜJØ]™[]™[ÜØ]™\ÛÝK™˜[YK]™[Ù]ÖÜØ]™\ÛÝK›˜[YKPVÓ�SQWÓSˆ H JNˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—Ó‘U×ÑÐSQWÓQS•Nˆ™]\›ˆØ]™\Û݈BˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H�[WÙY™šXÝ[Y\ΈBˆYŠÙ[XÝ܈ˆ�[WÙY™šXÝ[Y\ÊBˆˆÙ[XÝ܈H ˆBˆ ËÚYŠÙ[XÝÜ��[WÙY™šXÝ[Y\ÊHÛY\ˆHÙ[XÝ܈ -ˆ �N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠBˆ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆYŠÙ[XÝ܈OH�[WÙY™šXÝ[Y\ÊBˆˆ]Z]H NˆBˆ[ÙHYŠ›Û�\È�H]™[Ù]ÖÜÙ[XÝÜ—KšY˜ÛÛ\]JBˆˆØ]™\ÛÝHÙ[XÝ܎ˆÝ›˜ÜJØ]™[]™[ÜØ]™\ÛÝK™˜[YK]™[Ù]ÖÜØ]™\ÛÝK›˜[YKPVÓ�SQWÓSˆ H JNˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—Ó‘U×ÑÐSQWÓQS•Nˆ™]\›ˆØ]™\Û݈BˆBˆBˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—Ó‘U×ÑÐSQWÓQS•Nˆ™]\›ˆ LNŸB‚š[�ØYÜØ]™YÙØ[YJ -BžÂˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�Ø]™YÝ]\ÈH ˆÚ\ˆ˜[YVÓPVЕQ‘‘T—ÓS—HHȈŸNˆ[�ÛÛ HH N ÛÛ ˆH ŽÂ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÓÐQÑÐSQWÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆYŠ -Ø]™YÝ]\ÈHØYØ[YQš[J -JJBˆˆÙ]ZÓ˜[YJ˜[YK -NˆBˆ›ÜŠØ]™\ÛÝH ÈØ]™\ÛÝ�[WÙY™šXÝ[Y\ÎÈØ]™\ÛÝ -ÊÊHYŠØ]™[]™[ÜØ]™\ÛÝK™›YÈ ‰ˆØ]™[]™[ÜØ]™\ÛÝK›]™[ -Bˆˆœ™XZΈB‚ˆÚ[J\]Z] -BˆˆYŠØ]™\ÛÝ�H�[WÙY™šXÝ[Y\ÊH ËțݛÝ[™ˆˆÛY[�]^J ‹ M  Š“ØYØ[YHŠJNˆÛY[�]^ - ÛÛ K L‹Š”Ø]™Yš[NˆŠJNˆÛY[�]^ - ÛÛ ‹ L‹Š“›Ý›Ý[™HŠJNˆÛY[�]^J K ‹ Š�˜XÚÈŠJN‚ˆ Ê‚ˆܘ]\È - KLŒ � -HZ[›Üˆš^[ˆHÙ[XÝ܈�[X™\ˆYˆ›ÈØ]™H\È›Ý[™ ‚ˆ\™IÜÈ›ÈÙ[XÝ܈Œˆˆ[ˆ\ÈY[�KÛ›H  -ØY -H܈ H -˜XÚÊK‚ˆ -‹ÂˆÙ[XÝ܈H NˆBˆ[ÙBˆˆÛY[�]^J ‹ M  Š“ØYØ[YHŠJNˆÛY[�]^ - ÛÛ K L‹Š”Ø]™Yš[NˆŠJNˆYŠØ]™YÝ]\ÊBˆˆÛY[�]^ - ÛÛ ‹ L‹‰\È‹˜[YJNˆBˆ[ÙBˆˆÛY[�]^ - ÛÛ ‹ L‹Š“›Ý›Ý[™HŠJNˆB‚ˆYŠØ]™YÝ]\ÊBˆˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K LKŠ“[ÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ LK‰\È‹Ø]™[]™[ÜØ]™\ÛÝK™˜[YJNˆÛY[�]^ - ÛÛ K Š”ÝYÙNˆŠJNˆÛY[�]^ - ÛÛ ‹ ‰Y‹Ø]™[]™[ÜØ]™\ÛÝKœÝYÙJNˆÛY[�]^ - ÛÛ K KŠ“]™[ˆŠJNˆÛY[�]^ - ÛÛ ‹ K‰Y‹Ø]™[]™[ÜØ]™\ÛÝK›]™[ -NˆÛY[�]^ - ÛÛ K ‹Š�Ü™Y]ΈŠJN‚ˆYŠ›ÜÚ\™J^ˆÛY[�]^ - ÛÛ ‹ ‹‰Y ÉY ÉY ÉY‹ˆØ]™[]™[ÜØ]™\ÛÝKœÜ™Y]ÖÌKˆØ]™[]™[ÜØ]™\ÛÝKœÜ™Y]ÖÌWKØ]™[]™[ÜØ]™\ÛÝKœÜ™Y]ÖÌ—KˆØ]™[]™[ÜØ]™\ÛÝKœÜ™Y]ÖÌ×JNˆH[ÙHˆÛY[�]^ - ÛÛ ‹ ‹‰Y‹Ø]™[]™[ÜØ]™\ÛÝK˜Ü™Y]ÊNˆB‚ˆÛY[�]^ - ÛÛ K ËŠ”^Y\ˆ]™\ΈŠJNˆÛY[�]^ - ÛÛ ‹ ˉY ÉY ÉY ÉY‹ˆØ]™[]™[ÜØ]™\ÛÝKœ]™\ÖÌKˆØ]™[]™[ÜØ]™\ÛÝKœ]™\ÖÌWKØ]™[]™[ÜØ]™\ÛÝKœ]™\ÖÌ—KˆØ]™[]™[ÜØ]™\ÛÝKœ]™\ÖÌ×JNˆBˆÛY[�]^J -Ù[XÝ܈OH JK ‹ Š�˜XÚÈŠJNˆBˆ\]J  -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠÙ[XÝ܈OH  ‰ˆ -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘SQ• -JBˆˆÚ[J JBˆˆ K\Ø]™\Û݈YŠØ]™\ÛÝ -BˆˆØ]™\ÛÝH�[WÙY™šXÝ[Y\È H NˆBˆYŠØ]™[]™[ÜØ]™\ÛÝK™›YÈ ‰ˆØ]™[]™[ÜØ]™\ÛÝK›]™[ -Bˆˆœ™XZΈBˆBˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠÙ[XÝ܈OH  ‰ˆ -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -JBˆˆÚ[J JBˆˆ -ÊÜØ]™\Û݈YŠØ]™\Û݈�[WÙY™šXÝ[Y\È H JBˆˆØ]™\ÛÝH ˆBˆYŠØ]™[]™[ÜØ]™\ÛÝK™›YÈ ‰ˆØ]™[]™[ÜØ]™\ÛÝK›]™[ -Bˆˆœ™XZΈBˆBˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠØ]™YÝ]\ÊBˆˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H NˆBˆYŠÙ[XÝ܈ˆ JBˆˆÙ[XÝ܈H ˆBˆBˆ[ÙBˆˆÙ[XÝ܈H NˆB‚ˆYŠ -›Ý™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠJBˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆ™]\›ˆØ]™\Û݈œ™XZΈØ\ÙH N‚ˆ]Z]H Nˆœ™XZΈBˆBˆBˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÓÐQÑÐSQWÓQS•Nˆ™]\›ˆ LNŸB‚š[�ÚÛÜÙWÛ[ÙJ[� -œ^Y\œÊBžÂˆ[�]Z]H ˆ[�™[˜XÚÈH ˆ[�Ù[XÝ܈H ˆ[�Ý]\ÈH ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÑÐSQWÔÕT•ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆÚ[J\]Z] -BˆˆÛY[�]^J ‹ K Š�ÚÛÜÙH[ÙHŠJNˆÛY[�]^J -Ù[XÝ܈OH -K Ë Š“™]ÈØ[YHŠJNˆÛY[�]^J -Ù[XÝ܈OH JK  Š“ØYØ[YHŠJNˆÛY[�]^J -Ù[XÝ܈OH ŠK ‹ Š�˜XÚÈŠJN‚ˆ\]J  -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H ŽÂˆBˆYŠÙ[XÝ܈ˆ ŠBˆˆÙ[XÝ܈H ˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠBˆˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆÝ]\ÈHY[�WÙY™šXÝ[J -NˆYŠÝ]\ÈOH LJBˆˆ^YØ[YJ^Y\œËÝ]\Ë -Nˆ™[˜XÚÈH Nˆ]Z]H NˆBˆœ™XZΈØ\ÙH N‚ˆÝ]\ÈHØYÜØ]™YÙØ[YJ -NˆYŠÝ]\ÈOH LJBˆˆ^YØ[YJ^Y\œËÝ]\Ë JNˆ™[˜XÚÈH Nˆ]Z]H NˆBˆœ™XZΈY˜][‚ˆ]Z]H Nˆœ™XZΈBˆBˆBˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÑÐSQWÔÕT•ÓQS•Nˆ™]\›ˆ™[˜XÚΟB‚�›ÚY\›WÝšY[Û[Ù\Ê -BžÂˆšY[Û[Ù\Ëš™\ÈH ˆšY[Û[Ù\Ë�”™\ÈH ˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆYŠÝ\ÝØÙ[™\ÈOH•S -Bˆˆœ™YJÝ\ÝØÙ[™\ÊNˆBˆÝ\ÝØÙ[™\ÈH•SˆYŠÝ\ÝšÙÜ™ÈOH•S -Bˆˆœ™YJÝ\ÝšÙÜ™ÊNˆBˆÝ\ÝšÙÜ™ÈH•SˆYŠÝ\Ý]™[ÈOH•S -Bˆˆœ™YJÝ\Ý]™[ÊNˆBˆÝ\Ý]™[ÈH•SˆYŠÝ\Ý[Ù[ÈOH•S -Bˆˆœ™YJÝ\Ý[Ù[ÊNˆBˆÝ\Ý[Ù[ÈH•SŸB‚‹ËÈØYšY[È[ÙHœ›ÛHš[B�›ÚY[š]ÝšY[Û[Ù\Ê[�ÙÊBžÂˆÚ\ˆ -™š[[˜[YHH™]KÝšY[Ë�ŽÂˆ[�\ˆ™Y™—ÝÜË[ŽÂˆÚ^™WÝÚ^™NˆÚ\ˆ -˜�YˆH•SˆÚ\ˆ -˜ÛÛ[X[™H•SˆÚ\ˆ -�˜[YHH•S  -�˜[YLŽÂˆ\™Ó\Ý\™Û\݈Ú\ˆ\™Ø�Y–ÓPVÐT‘×ÓSˆ -È WHHˆŽÂ‚ˆ Ë‚ˆYŠÙÊBˆˆš[�Š’[š]X[^š[™ÈšY[Ë‹‹‹‹‹‹‹‹‹‹‹—ˆŠNˆB‚ˆ ËÈ\ÙH[ˆ[\›˜]]™HšY[Ë�Yˆ\™H\ÈÛ™KˆÛÛYHÙˆ\ÙH\™HۙȚ[[˜[Y\ÎÈÜ™X]H[Ý\ˆRÜÈÚ]›ÜœZÈ[™[ÝIÛ™Hš[™K‚ˆÙYš[™HžYš[J -HYŠ -\[Ü[œXÚÙš[J XÚÙš[JJHOKLJHÈÛÜÙ\XÚÙš[J\ -NÈš[[˜[YOVÈÛÝÈ™XYš[NÈBˆÚYˆÒSˆS•VˆžYš[J™]KÝšY[ÜË�ŠNˆÙ[™Y‚ˆÝ[™YˆžYš[B‚œ™XYš[N‚ˆ ËÈ™XYš[BˆYŠ�Y™™\—ÜZÙš[Jš[[˜[YK ˜�Y‹ œÚ^™JHOH JBˆˆšY[Ó[ÙHH ˆš[�ЉÉ\ÉțݛÝ[™ —ˆ‹š[[˜[YJNˆÛÝÈ’QSÓSÑTΈB‚ˆš[�Š”™XY[™ÈšY[ÈÙ][™ÜÈœ›ÛH É\ÉË—ˆ‹š[[˜[YJN‚ˆ ËÈ›ÝÈ[�\œ™]HÛÛ�[�ÈÙˆ�Yˆ[™HžH[™BˆÜÈH ˆÚ[JÜÈÚ^™JBˆˆ\œÙP\™ÜÊ ˜\™Û\Ý �Yˆ -ÈÜË\™Ø�YŠNˆÛÛ[X[™HÑUÐT‘Ê -N‚ˆYŠÛÛ[X[™ ‰ˆÛÛ[X[™ÌJBˆˆYŠÝšXÛ\ -ÛÛ[X[™ �šY[ÈŠHOH -Bˆˆ˜[YHHÑUÐT‘Ê JNˆYŠ -˜[YLˆHݘڊ˜[YK Þ ÊJJBˆˆšY[Û[Ù\Ëš™\ÈH]ÚJ˜[YJNˆšY[Û[Ù\Ë�”™\ÈH]ÚJ˜[YLˆ -È JNˆšY[Ó[ÙHH �MNˆBˆ[ÙBˆˆšY[Ó[ÙHHÑUÒS•ÐT‘Ê JNˆBˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ œØÙ[™\ÈŠHOH -Bˆˆ[ˆHÝ›[ŠÑUÐT‘Ê JJNˆÝ\ÝØÙ[™\ÈHX[ØÊ[ˆ -È JNˆÝ˜ÜJÝ\ÝØÙ[™\ËÑUÐT‘Ê JJNˆÝ\ÝØÙ[™\ÖÛ[—HH ˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ ˜˜XÚÙÜ›Ý[™ÈŠHOH -Bˆˆ[ˆHÝ›[ŠÑUÐT‘Ê JJNˆÝ\ÝšÙÜ™ÈHX[ØÊ[ˆ -È JNˆÝ˜ÜJÝ\ÝšÙÜ™ËÑUÐT‘Ê JJNˆÝ\ÝšÙÜ™ÖÛ[—HH ˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ ›]™[ÈŠHOH -Bˆˆ[ˆHÝ›[ŠÑUÐT‘Ê JJNˆÝ\Ý]™[ÈHX[ØÊ[ˆ -È JNˆÝ˜ÜJÝ\Ý]™[ËÑUÐT‘Ê JJNˆÝ\Ý]™[ÖÛ[—HH ˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ ›[Ù[ÈŠHOH -Bˆˆ[ˆHÝ›[ŠÑUÐT‘Ê JJNˆÝ\Ý[Ù[ÈHX[ØÊ[ˆ -È JNˆÝ˜ÜJÝ\Ý[Ù[ËÑUÐT‘Ê JJNˆÝ\Ý[Ù[ÖÛ[—HH ˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ ˜ÛÛÝ\™\ŠHOH -Bˆˆš[�Š—�ÛÛÜ™\\È›ÝÝ\Ü�Y[ž[[Ü™Kˆ[[Ù[\È\™H\Ü^YYÚ]H ̘š]ÛÛ܈ØÜ™Y[‹——ˆŠNˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ ™›Ü˜Ù[[ÙHŠHOH -HßBˆ[ÙHYŠÛÛ[X[™ ‰ˆÛÛ[X[™ÌJBˆˆš[�Š�ÛÛ[X[™ É\ÉÈ›Ý[™\œÝÛÙ[ˆš[H É\ÉÈWˆ‹ÛÛ[X[™ š[[˜[YJNˆBˆBˆ ËÈÛÈÈ™^[™BˆÜÈ -ÏHÙ]™]Ó[™TÝ\� -�Yˆ -ÈÜÊNˆB‚ˆYŠ�YˆOH•S -Bˆˆœ™YJ�YŠNˆ�YˆH•SˆB‚•’QSÓSÑT΂ˆšY[Û[Ù\Ë›[ÙHHšY[Ó[ÙNˆšY[Û[Ù\Ë™š[\ˆHØ]™Y]KœÝÙš[\ŽÂˆÝÚ]Ú -šY[Ó[ÙJBˆˆ ËÈ ÌŒ �  HU‘ÐBˆØ\ÙH ‚ˆšY[Û[Ù\Ëš™\ÈH ÌŒˆšY[Û[Ù\Ë�”™\ÈH � ˆšY[Û[Ù\ËšØØ[HH NˆšY[Û[Ù\Ë�”ØØ[HH NˆšY[Û[Ù\ËšÚY�H ˆšY[Û[Ù\Ë�”ÚY�H ˆšY[Û[Ù\˙ٙœÙ]H ŒÌNˆVQT—ÓRS—ÖˆH MŒˆVQT—ÓPVÖˆH ŒÌŽÂˆ‘ÒRQÒH MŒˆœ™XZ΂ˆ ËÈ   �̈ HÔU‘ÐBˆØ\ÙH N‚ˆšY[Û[Ù\Ëš™\ÈH  ˆšY[Û[Ù\Ë�”™\ÈH �̎ˆšY[Û[Ù\ËšØØ[HH -›Ø] -LK�NˆšY[Û[Ù\Ë�”ØØ[HH -›Ø] -LKŒLΈšY[Û[Ù\ËšÚY�H ˆšY[Û[Ù\Ë�”ÚY�H ŒˆšY[Û[Ù\˙ٙœÙ]H �ŒÎˆVQT—ÓRS—ÖˆH N ŽÂˆVQT—ÓPVÖˆH ��ˆ‘ÒRQÒH N ŽÂˆœ™XZ΂ˆ ËÈ �    H‘ÐBˆØ\ÙH Ž‚ˆšY[Û[Ù\Ëš™\ÈH � ˆšY[Û[Ù\Ë�”™\ÈH  ˆšY[Û[Ù\ËšØØ[HH ŽÂˆšY[Û[Ù\Ë�”ØØ[HH ŽÂˆšY[Û[Ù\ËšÚY�H MŒˆšY[Û[Ù\Ë�”ÚY�H ÍNˆšY[Û[Ù\˙ٙœÙ]H �ˆVQT—ÓRS—ÖˆH ÌŒNˆVQT—ÓPVÖˆH �Nˆ‘ÒRQÒH ÌŒNˆœ™XZ΂ˆ ËÈ ÌŒ   H‘ˆØ\ÙH ΂ˆšY[Û[Ù\Ëš™\ÈH ÌŒˆšY[Û[Ù\Ë�”™\ÈH  ˆšY[Û[Ù\ËšØØ[HH ‹Œ�NˆšY[Û[Ù\Ë�”ØØ[HH ŽÂˆšY[Û[Ù\ËšÚY�H Œ ˆšY[Û[Ù\Ë�”ÚY�H ÍNˆšY[Û[Ù\˙ٙœÙ]H �ˆVQT—ÓRS—ÖˆH ÌŒNˆVQT—ÓPVÖˆH �Nˆ‘ÒRQÒH ÌŒNˆœ™XZ΂ˆ ËÈ    HÕ‘ÐBˆØ\ÙH ‚ˆšY[Û[Ù\Ëš™\ÈH ˆšY[Û[Ù\Ë�”™\ÈH  ˆšY[Û[Ù\ËšØØ[HH ‹�NˆšY[Û[Ù\Ë�”ØØ[HH ŽÂˆšY[Û[Ù\ËšÚY�H � ˆšY[Û[Ù\Ë�”ÚY�H ÍNˆšY[Û[Ù\˙ٙœÙ]H �ˆVQT—ÓRS—ÖˆH ÌŒNˆVQT—ÓPVÖˆH �Nˆ‘ÒRQÒH ÌŒNˆœ™XZ΂ˆ ËÈ  Œ  HÕ‘ÐBˆØ\ÙH N‚ˆšY[Û[Ù\Ëš™\ÈH ˆšY[Û[Ù\Ë�”™\ÈH Œ ˆšY[Û[Ù\ËšØØ[HH ‹�NˆšY[Û[Ù\Ë�”ØØ[HH ‹�NˆšY[Û[Ù\ËšÚY�H � ˆšY[Û[Ù\Ë�”ÚY�H ˆšY[Û[Ù\˙ٙœÙ]H N ˆVQT—ÓRS—ÖˆH NˆVQT—ÓPVÖˆH N ŽÂˆ‘ÒRQÒH Nˆœ™XZ΂ˆ ËÈMŒ M  HRˆØ\ÙH Ž‚ˆšY[Û[Ù\Ëš™\ÈHMŒˆšY[Û[Ù\Ë�”™\ÈH M ˆšY[Û[Ù\ËšØØ[HH ΈšY[Û[Ù\Ë�”ØØ[HH ‹Œ�NˆšY[Û[Ù\ËšÚY�H ÌŒˆšY[Û[Ù\Ë�”ÚY�H ˆšY[Û[Ù\˙ٙœÙ]H LŒŽÂˆVQT—ÓRS—ÖˆH ÍŒŽÂˆVQT—ÓPVÖˆH L�ˆ‘ÒRQÒH ÍŒŽÂˆœ™XZ΂ˆ ËÈÝ\ÝÛHšY[È[ÙBˆØ\ÙH �MN‚ˆšY[Û[Ù\˙ٙœÙ]HšY[Û[Ù\Ë�”™\È -ˆ ŽMŒ�Nˆš[�Š—•\Ú[™ÈX�YÈšY[È[ÙNˆ Y Yˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ÊNˆœ™XZΈY˜][‚ˆ›Ü”Ú]ÝÛŠ K’[�˜[YšY[È[ÙNˆ Y[ˆ Ù]KÝšY[Ë� ËÝ\Ü�Y[Ù\Ηˆ‚ˆŒ H ÌŒ � ˆ‚ˆŒH H   �Ì—ˆ‚ˆŒˆ H �   ˆ‚ˆŒÈ H ÌŒ  ˆ‚ˆ� H   ˆ‚ˆ�H H  Œ ˆ‚ˆ�ˆ HMŒ M —ˆ‹šY[Ó[ÙJNˆœ™XZΈB‚ˆšY[×ÜÝ™]Ú -Ø]™Y]KœÝ™]Ú -N‚ˆYŠ -œØÜ™Y[ˆH[ØÜØÜ™Y[ŠšY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ËVSÌÌŠJHOH•S -Bˆˆ›Ü”Ú]ÝÛŠ K“›Ý[›ÝYÚY[[ÜžHWˆŠNˆBˆšY[Û[Ù\Ëœ^[H^[ž]\ÖÊ[� -]œØÜ™Y[‹Oœ^[›Ü›X]Nˆ ËÝšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆÛX\œØÜ™Y[ŠœØÜ™Y[ŠN‚ˆYŠÙÊBˆˆš[�Š’[š]X[^™YšY[Ë‹‹‹‹‹‹‹‹‹‹‹— Y Y -[ÙNˆ Y -W—ˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ËšY[Ó[ÙJNˆBŸB‚‚‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚‚‹ËÈÙ]Ù^H܈�]ÛˆØY™[H -Ú]ÝÚ]Ú[™ÊB�›ÚYØY™WÜÙ] -[� -˜\œ‹[�[™^ [�™]ÚÙ^K[�ÛÙ^JBžÂˆ[�Nˆ›ÜŠHH ÈH LŽÈJÊÊBˆˆYŠ\œ–ÚWHOH™]ÚÙ^JBˆˆ\œ–ÚWHHÛÙ^NˆBˆBˆ\œ–Ú[™^HH™]ÚÙ^NŸB‚�›ÚYÙ^X›Ø\™ÜÙ]\ -[�^Y\ŠBžÂˆ[�]Z]H Ȉ[�ÙYH ˆ[�Ù[XÝ܈H ˆ[�Ù][™ÈH LNˆ[�HH ˆ[�ÈH ˆ[�ÚÈH ˆ[�\ØX›YÙ^VÓPVЕ—Ó•SWHHÈ             HNˆ[�ÛÛ HH Nˆ[�ÛÛ ˆH ŽÂ‚ˆ™Y™—ݛٙœÙ]Ȉ™Y™—ÝÜ΂ˆÚ\Šˆ�YŽÂˆÚ\ŠˆÛÛ[X[™ˆÚ\Šˆš[[˜[YHH�˜[œÛ][Û‹ÛY[�K�ŽÂˆˆÚ\ˆ�]Û›˜[Y\ÖÓPVЕ—Ó•SWVÌÌ—NˆˆÚ^™WÝÚ^™Nˆ\™Ó\Ý\™Û\݈ˆÚ\ˆ\™Ø�Y–ÓPVÐT‘×ÓSˆ -È WHHˆŽÂˆ[�ÔSÓ”×Ó•SHHPVЕ—Ó•SH -È Î‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ЕUÓ—ÐÓÓ‘’Q×ÓQS•N‚ˆš[�Š“ØY[™ÈÛÛ�›ÛÙ][™ÜË‹‹‹‹‹‹—ŠN‚ˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÓSÕ‘UTK“[Ý™H\ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÓSÕ‘QÕÓ—K“[Ý™HÝÛˆŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÓSÕ‘SQ•K“[Ý™HY�ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÓSÕ‘T’QÒK“[Ý™HšYÚŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÐUPÒ×K�]XÚÈ HŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÐUPÒÌ—K�]XÚÈ ˆŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÐUPÒÌ×K�]XÚÈ ÈŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÐUPÒÍK�]XÚÈ ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÒ•STK’�[\ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÔÔPÒPSK”ÜXÚX[ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÔÕT•K”Ý\�ŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÔÐÔ‘QS”ÒÕK”ØÜ™Y[œÚÝŠNˆÝ˜ÜJ�]Û›˜[Y\ÖÔÑQÑTÐ×K‘^]ŠN‚ˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ‚ˆ Ê‚ˆܘ]\È - L LŒ ŒJHYY[ˆ[\›˜]]™HØØ][Ûˆ›ÜˆH˜[œÛ][Ûˆš[K›ÝÈ] ÜÈÜÜÚX›HÈ\ÙH[ˆ[ˆ^\›˜[›Û\‚ˆ›ÝÈH[Ù\ˆØ[ˆØY^Ü�Y˜[œÛ][Ûˆš[\ÈžH\Ú[™È™š[\Ý™X[HˆØÜš\�[˜Ý[ۜˆ\ÙY�[›ÜˆÜ™X][™ÈÝ\ÝÛH˜[œÛ][ÛœÈÚ]Ý][œXÚÈHØ[YBˆHY˜][[™Ú[™H˜[œÛ][ÛˆØØ][ÛˆÚ[™HXZ[�Z[™Y›Üˆ˜XÚÝØ\™ÛÛ\]Xš[]B‚ˆܘ]\È - LKLŒ ŒJH[�™\�YH]š[Üš]K›ÝÈH^\›˜[š[HÚ[Ý™\œšYHH[�\›˜[š[Bˆ\ÙY�[ÈXZ[�Z[ˆH[™Û\Ú˜[œÛ][Ûˆ[�XÝ[œÚYHHZÈš[HYˆ›ÈÝ\ˆ[™ÝXYÙHš[H\È›Ý[™[ˆH^\›˜[]ˆÝ\�Ú\ÙH[ÝHÚ[™YYțۘXÚÈH[™Û\Úš[H]™\žH[YH[›Ý\ˆ[™ÝXYÙH\È\ÙY[™[ˆ™[[Ý™Yˆ\ÈÜ\˜][Ûˆ\È™YYYÛ›HYˆH[™Û\Ú˜[œÛ][Ûˆš[H\Ù\ÈÛÛYHÝ\ÝÛHY[�H^ț܈[™Û\Ú[™ÝXYÙHÛˆ -‹ÂˆYŠ�Y™™\—ÜZÙš[Jš[[˜[YK ˜�Y‹ œÚ^™JHOH JBˆˆÛÝÈY˜][Ùš[NˆBˆ[ÙBˆˆÛÝÈ›ØÙYYˆB‚™Y˜][Ùš[N‚‚ˆYŠ�Y™™\—ÜZÙš[J™]KÛY[�K�‹ ˜�Y‹ œÚ^™JHOH JBˆˆÛÝÈš[š\ÚˆBˆ[ÙBˆˆÛÝÈ›ØÙYYˆB‚œ›ØÙYY‚‚ˆ ËÈ™XYš[Bˆ ËÈ›ÝÈ[�\œ™]HÛÛ�[�ÈÙˆ�Yˆ[™HžH[™BˆÜÈH ˆÚ[JÜÈÚ^™JBˆˆ\œÙP\™ÜÊ ˜\™Û\Ý �Yˆ -ÈÜË\™Ø�YŠNˆÛÛ[X[™HÑUÐT‘Ê -NˆYŠÛÛ[X[™ÌJBˆˆYŠÝšXÛ\ -ÛÛ[X[™ ™\ØX›ZÙ^HŠHOH -BˆˆÙYH˜[œÛ]WÔÑQ -ÑUÐT‘Ê JJNˆYŠÙY�H -Bˆˆ\ØX›YÙ^VÜÙYHH NˆBˆBˆ[ÙHYŠÝšXÛ\ -ÛÛ[X[™ œ™[˜[YZÙ^HŠHOH -BˆˆÙYH˜[œÛ]WÔÑQ -ÑUÐT‘Ê JJNˆYŠÙY�H -BˆˆÝ˜ÜJ�]Û›˜[Y\ÖÜÙYKÑUÐT‘Ê ŠJNˆBˆB‚ˆBˆ ËÈÛÈÈ™^[™BˆÜÈ -ÏHÙ]™]Ó[™TÝ\� -�Yˆ -ÈÜÊNˆBˆYŠ�YˆOH•S -Bˆˆœ™YJ�YŠNˆ�YˆH•SˆB‚™š[š\Ú‚‚ˆÚ[J\ØX›YÙ^VÜÙ[XÝÜ—JHYŠ -ÊÜÙ[XÝ܈ˆPVЕ—Ó•SH H JHœ™XZ΂ˆÚ[J\]Z] -Bˆˆ›Ù™œÙ]H MŽÂˆÛY[�]^J ‹ N  Š”^Y\ˆ ZHŠK^Y\ˆ -È JNˆ›ÜŠHH ÈHPVЕ—Ó•SNÈJÊÊBˆˆYŠY\ØX›YÙ^VÚWJBˆˆÛY[�]^ - -Ù[XÝ܈OHJKÛÛ K›Ù™œÙ] ‰\È‹�]Û›˜[Y\ÖÚWJNˆÛY[�]^ - -Ù[XÝ܈OHJKÛÛ ‹›Ù™œÙ] ‰\È‹ÛÛ�›ÛÙÙ]Ù^[˜[YJØ]™Y]KšÙ^\ÖÜ^Y\—VÚWJJNˆ›Ù™œÙ] -ÊΈBˆB‚ˆ -ÊݛٙœÙ]ˆYŠØ]™Y]Kš›Þ\�[X›VÜ^Y\—JBˆˆÛY[�]^ - -Ù[XÝ܈OHÔSÓ”×Ó•SH H ÊKÛÛ K›Ù™œÙ] -ÊËŠ”�[X›H[˜X›YŠJNˆBˆ[ÙBˆˆÛY[�]^ - -Ù[XÝ܈OHÔSÓ”×Ó•SH H ÊKÛÛ K›Ù™œÙ] -ÊËŠ”�[X›H\ØX›YŠJNˆB‚ˆÛY[�]^J -Ù[XÝ܈OHÔSÓ”×Ó•SH H ŠK -ÊݛٙœÙ]  Š“ÒÈŠJNˆÛY[�]^J -Ù[XÝ܈OHÔSÓ”×Ó•SH H JK -ÊݛٙœÙ]  Š�Ø[˜Ù[ŠJNˆÛY[�]^J -Ù[XÝ܈OHÔSÓ”×Ó•SJK -ÊݛٙœÙ]  Š‘Y˜][ŠJNˆ\]J -]™[OH•S -K -N‚ˆYŠÙ][™Èˆ LJBˆˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆØ]™Y]KšÙ^\ÖÜ^Y\—VÜÙ][™×HHÚΈÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆÙ][™ÈH LNˆBˆYŠÙ][™Èˆ LJBˆˆÈHÛÛ�›ÛÜØØ[šÙ^J -NˆYŠÊBˆˆØY™WÜÙ] -Ø]™Y]KšÙ^\ÖÜ^Y\—KÙ][™ËËÚÊNˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆÙ][™ÈH LNˆ ËÈ™]™[�XØÚY[�[ØÜ™Y[œÚ݈›Ý™]ÚÙ^\ÈH ˆBˆBˆBˆ[ÙBˆˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -BˆˆˆˆYŠ K\Ù[XÝ܈ -Bˆˆœ™XZΈBˆBˆÚ[JÙ[XÝ܈PVЕ—Ó•SH ‰ˆ\ØX›YÙ^VÜÙ[XÝÜ—JNˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆˆˆYŠ -ÊÜÙ[XÝ܈ˆPVЕ—Ó•SH H JHœ™XZΈBˆÚ[J\ØX›YÙ^VÜÙ[XÝÜ—JNˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈HÔSÓ”×Ó•SNˆBˆYŠÙ[XÝ܈ˆÔSÓ”×Ó•SJBˆˆÙ[XÝ܈H ˆÚ[J\ØX›YÙ^VÜÙ[XÝÜ—JHYŠ -ÊÜÙ[XÝ܈ˆPVЕ—Ó•SH H JHœ™XZΈB‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -N‚ˆYˆ -Ù[XÝ܈OHÔSÓ”×Ó•SH H È ‰‚ˆ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ -JHÛÛ�[�YN‚ˆYŠÙ[XÝ܈OHÔSÓ”×Ó•SH H ÊBˆˆØ]™Y]Kš›Þ\�[X›VÜ^Y\—H�H NˆBˆ[ÙHYŠÙ[XÝ܈OHÔSÓ”×Ó•SH H ŠH ËÈÒˆˆ]Z]H ŽÂˆBˆ[ÙHYŠÙ[XÝ܈OHÔSÓ”×Ó•SH H JH ËÈØ[˜Ù[ˆˆ]Z]H NˆBˆ[ÙHYŠÙ[XÝ܈OHÔSÓ”×Ó•SJH ËÈY˜][ˆˆÛX\˜�]ÛœÊ^Y\ŠNˆBˆ[ÙBˆˆÙ][™ÈHÙ[XÝ܎ˆÚÈHØ]™Y]KšÙ^\ÖÜ^Y\—VÜÙ][™×NˆØ]™Y]KšÙ^\ÖÜ^Y\—VÜÙ][™×HH ˆÙ^X›Ø\™ÙÙ]\ÝÙ^J -NˆBˆBˆBˆB‚ˆYŠ]Z]OH ŠBˆˆ\WØÛÛ�›ÛÊ -NˆØ]™\Ù][™ÜÊ -NˆBˆ[ÙBˆˆØYÙ][™ÜÊ -NˆB‚ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ЕUÓ—ÐÓÓ‘’Q×ÓQS•N‚ˆ\]J  -Nˆ›Ý™]ÚÙ^\ÈH ˆš[�Š‘Û™HWˆŠNŸB‚�›ÚYY[�WÛÜ[Ûœ×Ú[œ] - -BžÂˆ[�]Z]H ˆ[�Ù[XÝ܈H NÈ ËÈ ˆ[�ÜÜÈH MŽÂˆÚYˆS‘“ÒQˆ[�ÔSÓ”×Ó•SHH ŽÂˆÙ[ÙBˆ[�ÔSÓ”×Ó•SHH NˆÙ[™Y‚‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÐÓÓ•“ÓÓÔSÓ”×ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆ ËÈܘ]\È - L LŒ ŒJHYYHÙXÛÛ™[œÝ[˜ÙHÙˆH˜ÛÛ�›ÛÚ[š]ˆ�[˜Ý[ÛˆÚ[H[ˆHÛÛ�›ÛÜ[ۜˆ ËÈ\ÙY�[È™Yœ™\ÚÛÛYH^˜[œÛ][ÛˆYˆH[™ÝXYÙH\ÈÚ[™ÙY›Û‹]KY›Hˆ[™™KY]XÝ[XÝ]™HÛÛ�›ÛˆÛÛ�›ÛÚ[š] -Ø]™Y]K�\ÙZ›ÞJN‚ˆÚ[J\]Z] -BˆˆÛY[�]^J ‹ÜÜËLK Š�ÛÛ�›ÛÜ[ۜȊJNˆYŠØ]™Y]K�\ÙZ›ÞJBˆˆÛY[�]^ - -Ù[XÝ܈OH -KÜÜË L‹Š‘Ø[YTYÈ[˜X›YŠJNˆYŠXÛÛ�›ÛÙÙ]›ÞY[˜X›Y - -JBˆˆÛY[�]^ - -Ù[XÝ܈OH -KÜÜÊÌLK L‹Šˆ H]šXÙH›Ý™XYHŠJNˆBˆBˆ[ÙBˆˆÛY[�]^ - -Ù[XÝ܈OH -KÜÜË L‹Š‘Ø[YTYÈ\ØX›YŠJNˆBˆÛY[�]^ - -Ù[XÝ܈OH JKÜÜË LKŠ”Ù]\^Y\ˆ K‹‹ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ŠKÜÜË Š”Ù]\^Y\ˆ ‹‹‹ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ÊKÜÜË KŠ”Ù]\^Y\ˆ Ë‹‹ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÜÜË ‹Š”Ù]\^Y\ˆ ‹‹ˆŠJNˆÚYˆS‘“ÒQˆYŠØ]™Y]Kš\×ÝÝXÚYÝšXœ˜][Û—Ù[˜X›Y -BˆˆÛY[�]^J -Ù[XÝ܈OH JK  Š•ÝXÚYšXœ˜][Ûˆ[˜X›YŠJNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OH JK  Š•ÝXÚYšXœ˜][Ûˆ\ØX›YŠJNˆBˆÛY[�]^J -Ù[XÝ܈OH ŠK ‹ Š�˜XÚÈŠJNˆÙ[ÙBˆÛY[�]^J -Ù[XÝ܈OH JK K Š�˜XÚÈŠJNˆÙ[™Y‚‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈HÔSÓ”×Ó•SNˆBˆYŠÙ[XÝ܈ˆÔSÓ”×Ó•SJBˆˆÙ[XÝ܈H ˆBˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆÛÛ�›ÛÝ\ÙZ›ÞJ -Ø]™Y]K�\ÙZ›ÞH�H JJNˆœ™XZΈØ\ÙH N‚ˆÙ^X›Ø\™ÜÙ]\ - -Nˆœ™XZΈØ\ÙH Ž‚ˆÙ^X›Ø\™ÜÙ]\ - JNˆœ™XZΈØ\ÙH ΂ˆÙ^X›Ø\™ÜÙ]\ - ŠNˆœ™XZΈØ\ÙH ‚ˆÙ^X›Ø\™ÜÙ]\ - ÊNˆœ™XZΈÚYˆS‘“ÒQˆØ\ÙH N‚ˆØ]™Y]Kš\×ÝÝXÚYÝšXœ˜][Û—Ù[˜X›Y�H Nˆœ™XZΈÙ[™Y‚ˆY˜][‚ˆ]Z]H -›Ý™]ÚÙ^\È ˆ“Q×ÐS–P•UÓŠNˆBˆBˆBˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÐÓÓ•“ÓÓÔSÓ”×ÓQS•NŸB‚‚‹Ê‚ˆܘ]\È - ‹LŒ ŒÊH™XY�\ÝYHX\Ý\ˆ ÈY™™XÝ›Û È]\ÚXݛۘ[™ÙH[™[˜Ü™[Y[�ˆ›Ý[™˜[Y\ÈZÙH L žHY˜][XZÙH[Ü™HÙ[œÙH[ˆÛÛYHZÙH Ì ÌLŒˆÛËH˜[œÙ™\œ™YH\™ÛÙY›Û[YHY�\ÝY[�ÈHœÛÝ[™Z^ˆÛÙH[œÝXYÙˆÚ[™Ú[™ÈHY˜][›Û[Y\ˆX\Ý\ˆ›ÝÈÛÙ\Èœ›ÛH È L [™Y˜][È L ˆY™™XÝÈ -Ø[\\ÊH›ÝÈÛÙ\Èœ›ÛH È L [™Y˜][È L ˆ]\ÚXÈ›ÝÈÛÙ\Èœ›ÛH È Œ [™Y˜][È L Š‹Â�›ÚYY[�WÛÜ[Ûœ×ÜÛÝ[™ - -BžÂˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�\ŽÂˆ[�ÛÛ HH Nˆ[�ÛÛ ˆH ŽÂ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÔÓÕS‘ÓÔSÓ”×ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆÚ[J\]Z] -BˆˆÛY[�]^J ‹ MK Š”ÛÝ[™Ü[ۜȊJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K L‹Š“X\Ý\ˆ›Û[YNˆŠJNÈ ËÈܘ]\È - ‹LŒ ŒÊH™[˜[YYœ›ÛHÛÝ[™ÈX\Ý\ˆ›Û[YBˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ L‹‰ZH‹Ø]™Y]KœÛÝ[™›Û -NˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ K LKŠ‘Y™™XÝÈ›Û[YNˆŠJNÈ ËÈܘ]\È - ‹LŒ ŒÊH™[˜[YYœ›ÛHÑ–ÈY™™XÝÈ›Û[YBˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ ‹ LK‰ZH‹Ø]™Y]K™Y™™XÝ›Û -NˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ K Š“]\ÚXÈ›Û[YNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ ‹ ‰ZH‹Ø]™Y]K›]\ÚXÝ›Û -NˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ K KŠ�‘ÓNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ ‹ K -Ø]™Y]K�\Ù[]\ÚXÈÈŠ‘[˜X›YŠHˆŠ‘\ØX›YŠJJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K ‹Š”ÚÝÈ]\ΈŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ ‹ -Ø]™Y]KœÚÝÝ]\ÈÈŠ–Y\ÈŠHˆŠ“›ÈŠJJNˆÛY[�]^J -Ù[XÝ܈OH JK K Š�˜XÚÈŠJN‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H NˆBˆYŠÙ[XÝ܈ˆ JBˆˆÙ[XÝ܈H ˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆˆ\ˆH ‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘SQ• -Bˆˆ\ˆH LNˆBˆ[ÙHYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -Bˆˆ\ˆH NˆB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ËÈܘ]\È - ‹LŒ ŒÊH™]ÛÜšÙYHÛ[™[�\ÙY�[œÛÝ[™›ÛˆÈXÝ\ÈHX\Ý\ˆ›Û[YH›Üˆ›ÝÛÝ[™ Û]\ÚXˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆØ]™Y]KœÛÝ[™›Û -ÏH H -ˆ\ŽÂˆYŠØ]™Y]KœÛÝ[™›Û -BˆˆØ]™Y]KœÛÝ[™›ÛH ˆBˆYŠØ]™Y]KœÛÝ[™›Ûˆ L -BˆˆØ]™Y]KœÛÝ[™›ÛH L ˆBˆЗÜÙ]›Û[YJЗՓÒPÑU“Ó Ø]™Y]KœÛÝ[™›Û -N‚ˆ ËÈܘ]\È - ‹LŒ ŒÊHÞ[˜Ú›Ûš^™HÛÝ[™ Û]\ÚXÈ›Û[Y\ÈÈX\Ý\‚ˆØ]™Y]K™Y™™XÝ›ÛHØ]™Y]KœÛÝ[™›ÛˆØ]™Y]K›]\ÚXÝ›ÛHØ]™Y]KœÛÝ[™›Ûˆœ™XZΈØ\ÙH N‚ˆØ]™Y]K™Y™™XÝ›Û -ÏH H -ˆ\ŽÂˆYŠØ]™Y]K™Y™™XÝ›Û -BˆˆØ]™Y]K™Y™™XÝ›ÛH ˆBˆYŠØ]™Y]K™Y™™Xݛۈ L -BˆˆØ]™Y]K™Y™™XÝ›ÛH L ˆBˆœ™XZΈØ\ÙH ΂ˆYŠY\ŠBˆˆœ™XZΈBˆYŠ\Ø]™Y]K�\Ù[]\ÚXÊBˆˆØ]™Y]K�\Ù[]\ÚXÈH Nˆ]\ÚXÊ™]KÛ]\ÚXËÜ™[Z^‹ K -NˆBˆ[ÙBˆˆØ]™Y]K�\Ù[]\ÚXÈH ˆÛÝ[™ØÛÜÙWÛ]\ÚXÊ -NˆBˆœ™XZΈØ\ÙH Ž‚ˆØ]™Y]K›]\ÚXÝ›Û -ÏH H -ˆ\ŽÂˆYŠØ]™Y]K›]\ÚXÝ›Û -BˆˆØ]™Y]K›]\ÚXÝ›ÛH ˆBˆYŠØ]™Y]K›]\ÚXݛۈ Œ -BˆˆØ]™Y]K›]\ÚXÝ›ÛH Œ ˆBˆÛÝ[™Ý›Û[YWÛ]\ÚXÊØ]™Y]K›]\ÚXÝ›Û Ø]™Y]K›]\ÚXÝ›Û -Nˆœ™XZΈØ\ÙH ‚ˆØ]™Y]KœÚÝÝ]\ÈH\Ø]™Y]KœÚÝÝ]\Έœ™XZΈY˜][‚ˆ]Z]H NˆBˆBˆBˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÔÓÕS‘ÓÔSÓ”×ÓQS•NŸB‚�›ÚYY[�WÛÜ[Ûœ×ØÛÛ™šYÊ -H ËÈÖ ˆØYœ›ÛH ÈØ]™HÈY˜][ ˜Ù™Ëˆ™\ÝÜ™HÜ[�›Ôˆ™˜XÝÜžHˆÙ][™ÜË‚žÂˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�Ø]™YH ˆ[�ØYYH ˆ[�™\ÝÜ™YH ‚ˆ›Ý™]ÚÙ^\ÈH ‚ˆ ËÈܘ]\È - LKLŒ ŒJH™[[Ý™YÜXÙ\ÈÛˆHˆÛ™HHˆ^Èš^˜[œÛ][Û‚ˆÚ[J\]Z] -BˆˆÛY[�]^J ‹ MK Š�ÛÛ™šYÝ\˜][ÛˆÙ][™ÜÈŠJN‚ˆYŠØ]™YOH JBˆˆÛY[�]^J -Ù[XÝ܈OH -K LË Š”Ø]™HÙ][™ÜÈÈY˜][ ˜Ù™É\ÈŠKŠˆÛ™HHŠJNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OH -K LË Š”Ø]™HÙ][™ÜÈÈY˜][ ˜Ù™É\ÈŠKˆŠNˆB‚ˆYŠØYYOH JBˆˆÛY[�]^J -Ù[XÝ܈OH JK L‹ Š“ØYÙ][™ÜÈœ›ÛHY˜][ ˜Ù™É\ÈŠKŠˆÛ™HHŠJNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OH JK L‹ Š“ØYÙ][™ÜÈœ›ÛHY˜][ ˜Ù™É\ÈŠKˆŠNˆB‚ˆYŠ™\ÝÜ™YOH JBˆˆÛY[�]^J -Ù[XÝ܈OH ŠK LK Š”™\ÝÜ™HÜ[�›ÔˆY˜][É\ÈŠKŠˆÛ™HHŠJNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OH ŠK LK Š”™\ÝÜ™HÜ[�›ÔˆY˜][É\ÈŠKˆŠNˆB‚ˆÛY[�]^J -Ù[XÝ܈OH ÊK ‹ Š�˜XÚÈŠJN‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆB‚ˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H ΈBˆYŠÙ[XÝ܈ˆ ÊBˆˆÙ[XÝ܈H ˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆØ]™X\ÙY˜][ - -NˆØ]™YH Nˆœ™XZ΂ˆØ\ÙH N‚ˆØYœ›ÛYY˜][ - -Nˆ ËØ›Ü”Ú]ÝÛŠ ‹—”Ù][™ÜÈØYYœ›ÛHY˜][ ˜Ù™Ëˆ™\Ý\�™\]Z\™Y ——ˆŠNˆ[š]ÝšY[Û[Ù\Ê -NˆYŠ]šY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊJBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈÙ]šY[È[ÙNˆ Y YWˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ÊNˆBˆЗÜÙ]›Û[YJЗՓÒPÑU“Ó Ø]™Y]KœÛÝ[™›Û -NˆÛÝ[™Ý›Û[YWÛ]\ÚXÊØ]™Y]K›]\ÚXÝ›Û Ø]™Y]K›]\ÚXÝ›Û -NˆØYYH Nˆœ™XZΈØ\ÙH Ž‚ˆÛX\œÙ][™ÜÊ -Nˆ ËØ›Ü”Ú]ÝÛŠ ‹—”Ù][™ÜÈØYYœ›ÛHY˜][ ˜Ù™Ëˆ™\Ý\�™\]Z\™Y ——ˆŠNˆ[š]ÝšY[Û[Ù\Ê -NˆYŠ]šY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊJBˆˆ›Ü”Ú]ÝÛŠ K•[˜X›HÈÙ]šY[È[ÙNˆ Y YWˆ‹šY[Û[Ù\Ëš™\ËšY[Û[Ù\Ë�”™\ÊNˆBˆЗÜÙ]›Û[YJЗՓÒPÑU“Ó Ø]™Y]KœÛÝ[™›Û -NˆÛÝ[™Ý›Û[YWÛ]\ÚXÊØ]™Y]K›]\ÚXÝ›Û Ø]™Y]K›]\ÚXÝ›Û -Nˆ™\ÝÜ™YH Nˆœ™XZΈY˜][‚ˆ]Z]H NˆBˆBˆBˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËL ‚Š‚Šˆ™]\›ˆY[�H\Ü^H^›ÜˆHÛÛ\Ú[ÛˆX�YŠˆ\Ü^HZ\‹‚Š‹ÂœÝ]XÈÚ\ŠˆÛÛ\Ú[Û—ÙX�Y×Ý\WÜÝš[™ÊWÙX�Y×Ù\Ü^HX�Y×ÙšY[ WÙX�Y×Ù\Ü^H›]Øš] WÙX�Y×Ù\Ü^H›Ú™XÝYØš] -H‚ˆYˆ - -X�Y×ÙšY[ ˆ›]Øš] -H ‰ˆ -X�Y×ÙšY[ ˆ›Ú™XÝYØš] -JHˆ™]\›ˆŠ‘›] -țڙXÝYŠNˆH[ÙHYˆ -X�Y×ÙšY[ ˆ›]Øš] -Hˆ™]\›ˆŠ‘›]ŠNˆH[ÙHYˆ -X�Y×ÙšY[ ˆ›Ú™XÝYØš] -Hˆ™]\›ˆŠ”›Ú™XÝYŠNˆH[ÙHˆ™]\›ˆŠ‘\ØX›YŠNˆBŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ �‹L ËL ‚Š‚ŠˆÞXÛHÛÛ\Ú[ÛˆX�YÈ\Ü^H[ÙH›ÜˆÛ™HÛÛ\Ú[Ûˆ\K‚Š‚ŠˆXXÚÛÛ\Ú[Ûˆ\H\Ù\ÈÛÈX�YÈš]Î‚Š‚Šˆ›]Øš]H˜]ÈH›Ü›X[ ‘™XÝ[™ÛHÝ™\›^K‚Šˆ›Ú™XÝYØš]H˜]ÈH›Ú™XÝY ÑÝX™HÝ™\›^K‚Š‚ŠˆHÛÈš]ÈÛÛXš[™H[�È›Ý\ˆÜÜÚX›HÝ]\Î‚Š‚Šˆ H›Û™BŠˆ HH›]Šˆ ˆH›Ú™XÝYŠˆ ÈH›] -țڙXÝYŠ‚Šˆ\™XÝ[ÛˆÛÛ�›ÛÈÞXÛHÜ™\Ž‚Š‚ŠˆÑÑÓWÑT‘PÕSÓ—Ð�PÒÕÐT‘HÞXÛH˜XÚÝØ\™ ‚ŠˆÑÑÓWÑT‘PÕSӗѓԕÐT‘HÞXÛH›Ü�Ø\™ ‚Š‚Šˆ™]\›ˆ˜[YH\ÈH\]YX�YÈš]šY[ ‚Š‹ÂœÝ]XÈWÙX�Y×Ù\Ü^HÛÛ\Ú[Û—ÙX�Y×Ý\WØÞXÛJÛÛœÝWÙX�Y×Ù\Ü^HX�Y×ÙšY[ ÛÛœÝWÙX�Y×Ù\Ü^H›]Øš] ÛÛœÝWÙX�Y×Ù\Ü^H›Ú™XÝYØš] ÛÛœÝWÛY[�WÝÙÙÛWÙ\™XÝ[ÛˆÙÙÛWÙ\™XÝ[ÛŠH‚ˆ Ê‚ˆ -ˆØØ[Û›KˆXÝX[Ý]H\ÈÛÛ�›ÛYžBˆ -ˆHØ]™Y]K™X�YÚ[™›Èš]šY[ ˆ\È�\݈ -ˆÚ]™\È\ÈHÚ[\H[™X\ˆ˜[YHÈÛÜšÈÚ]ˆ -ˆ›ÜˆÞXÛ[™ÈÛÈÙHÉÝ™YYHX\ÜÚ]™HY‹Ù[ÙBˆ -ˆÚZ[‹‚ˆ -‹ÂˆÙYš[™HÓÓTÒSÓ—ÑP•Q×ÑTÔVWÓ“Ó‘H ˆÙYš[™HÓÓTÒSÓ—ÑP•Q×ÑTÔVWÑ“U BˆÙYš[™HÓÓTÒSÓ—ÑP•Q×ÑTÔVWÔ“Ò‘PÕQ ‚ˆÙYš[™HÓÓTÒSÓ—ÑP•Q×ÑTÔVWГÕ È‚ˆ Ê‚ˆ -ˆ[�ØÜ˜]ÚYˈ\ÙH\™H[�[H˜[Y\Ë�]ÛÛ\[\‚ˆ -ˆÚ[›ÝÈHš]YˆÙHžHÈ\ÙH[H[ˆÜ\˜][ۜˆ -ˆ]Ø]\ÙH[\XÚ][�YÙ\ˆ›Û[Ý[Û‹‚ˆ -‹Âˆ[�Ý]Nˆ[�X�Y×Ù›YÜ΂ˆ Ê‚ˆ -ˆÛÜšÈ[ˆ[ˆ[�ØÜ˜]Ú˜[YH™XØ]\ÙH[�[H˜[Y\È\™Bˆ -ˆÝ[š]šY[Ë[™š]Ú\ÙHÜ\˜]ܜțÛ[ÝH[Bˆ -ˆÈ[�[ž]Ø^K‚ˆ -‹ÂˆX�Y×Ù›YÜÈHX�Y×ÙšY[‚ˆ Ê‚ˆ -ˆÛÛ�™\�Ý\œ™[�š]šY[Ý]H[�ÈHÛÛ\X݈ -ˆ\Ü^H[ÙH˜[YK‚ˆ -‚ˆ -ˆš] H›]\Ü^H[˜X›Y ‚ˆ -ˆš] HH›Ú™XÝY\Ü^H[˜X›Y ‚ˆ -‹ÂˆÝ]HHÓÓTÒSÓ—ÑP•Q×ÑTÔVWÓ“Ó‘N‚ˆYˆ -X�Y×Ù›YÜÈ ˆ›]Øš] -HˆÝ]HHÓÓTÒSÓ—ÑP•Q×ÑTÔVWÑ“UˆB‚ˆYˆ -X�Y×Ù›YÜÈ ˆ›Ú™XÝYØš] -HˆÝ]HHÓÓTÒSÓ—ÑP•Q×ÑTÔVWÔ“Ò‘PÕQˆB‚ˆ Ê‚ˆ -ˆÝ\›ÝYÚH›Ý\ˆ\Ü^H[Ù\Ë‚ˆ -‚ˆ -ˆ˜XÚÝØ\™‚ˆ -ˆ›Û™H H›] H›Ú™XÝY H›Ý H›Û™Bˆ -‚ˆ -ˆ›Ü�Ø\™‚ˆ -ˆ›Û™H Oˆ›] Oˆ›Ú™XÝY Oˆ›Ý Oˆ›Û™Bˆ -‹ÂˆÝÚ]Ú -ÙÙÛWÙ\™XÝ[ÛŠHˆØ\ÙHÑÑÓWÑT‘PÕSÓ—Ð�PÒÕÐT‘‚ˆÝ]KKN‚ˆYˆ -Ý]HÓÓTÒSÓ—ÑP•Q×ÑTÔVWÓ“Ó‘JHˆÝ]HHÓÓTÒSÓ—ÑP•Q×ÑTÔVWГÕˆB‚ˆœ™XZ΂ˆØ\ÙHÑÑÓWÑT‘PÕSӗѓԕÐT‘‚ˆY˜][‚ˆÝ]JÊ΂ˆYˆ -Ý]HˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWГÕ -HˆÝ]HHÓÓTÒSÓ—ÑP•Q×ÑTÔVWÓ“Ó‘NˆB‚ˆœ™XZΈB‚ˆ Ê‚ˆ -ˆÛX\ˆÛ›HHÛÈš]ÈX[˜YÙYžH\ÈÛÛ\Ú[Û‚ˆ -ˆX�YÈÜ[Û‹ˆX]™H[Ý\ˆX�YÈ›YÜÈ[�ÝXÚY ‚ˆ -‹ÂˆX�Y×Ù›YÜÈ �HŠ›]Øš]›Ú™XÝYØš] -N‚ˆ Ê‚ˆ -ˆ^[™ÛÛ\XÝÝ]H˜XÚÈ[�ÈHX�YÈš]šY[ ‚ˆ -‹ÂˆYˆ -Ý]H ˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWÑ“U -HˆX�Y×Ù›YÜÈH›]Øš]ˆB‚ˆYˆ -Ý]H ˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWÔ“Ò‘PÕQ -HˆX�Y×Ù›YÜÈH›Ú™XÝYØš]ˆB‚ˆ™]\›ˆ -WÙX�Y×Ù\Ü^JYX�Y×Ù›YÜ΂ˆÝ[™YˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWÓ“Ó‘BˆÝ[™YˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWÑ“UˆÝ[™YˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWÔ“Ò‘PÕQˆÝ[™YˆÓÓTÒSÓ—ÑP•Q×ÑTÔVWГÕŸB‚�›ÚYY[�WÛÜ[Ûœ×ÙX�YÊ -HˆÙYš[™HQS•WÔÔ×ÖH MˆÙYš[™HQS•WÒUST×ÓPT‘ÒS—ÖH ‚ˆÙYš[™HÓÓSS—ÌWÔÔ×Ö LLBˆÙYš[™HÓÓSS—Ì—ÔÔ×ÖÓÓSS—ÌWÔÔ×Ö -È MˆÙYš[™HQS•WÒUSWÑ’T”ÕÒS‘V ‚ˆ ËÈÙ[XÝ[ÛœÈ[�[Y\˜]Ü‹ˆ[ˆ ËÈÙ[XÝ[Ûˆ][\ÈÚÝ[™HXÙYˆ ËÈ\™Hš\œÝ ‚ˆ\YYˆ[�[H‚ˆ ËÈš\œÝ][HØ[ˆ™Bˆ ËÈÚ]]™\ˆÙHZÙKˆ ËÈ�]]]\Ý™HÙ]ˆ ËÈÈHQS•WÒUSWÑ’T”ÕÒS‘Vˆ ËÈÛÛœÝ[� ‚ˆUSWÔT‘“Ô“PS�ÑHHQS•WÒUSWÑ’T”ÕÒS‘V ‚ˆ ËÈ][\È™]ÙY[ˆHš\œÝˆ ËÈ[™\ÝØ[ˆÛÈ[ˆ[žBˆ ËÈÜ™\‹‚ˆUSWÔÔÒUSÓ‹ˆUSWÐÓÓÐUPÒˈUSWÐÓÓГÑKˆUSWÐÓÓÔÔPÑKˆUSWÐÓÓÔ�S‘ÑK‚ˆ ËÈ\È\ÈH�˜XÚÈ‚ˆ ËÈÙ[XÝ[Ûˆ[™ÚÝ[ˆ ËÈ[Ø^\È™H\Ý ‚ˆUSWÑVUˆHWÜÙ[XÝ[ۜ΂ˆ[�Ü×ÞNˆ[�]Z]H ˆWÜÙ[XÝ[ÛœÈÙ[XÝ܈H ˆ›Ý™]ÚÙ^\ÈH ˆˆWÛY[�WÝÙÙÛWÙ\™XÝ[ÛˆÙÙÛWÙ\™XÝ[ÛˆHÑÑÓWÑT‘PÕSӗѓԕÐT‘‚ˆÚ[J\]Z] -H‚ˆ ËÈ\Ü^HY[�H]K‚ˆÛY[�]^J ‹QS•WÔÔ×ÖK Š‘X�YÈÙ][™ÜÈŠJN‚ˆ ËÈY[�H][\Ë‚ˆ ËÈHÜÚ][Ûˆ\ÈÛÛ�›ÛYžHH[˜Ü™[Y[�Y[�YÙ\‹‚ˆ ËÈ[�YÙ\ˆ\ÈH˜\ÙH˜[YHÙˆHY[�H]HBˆ ËÈ\ÈÝ]XÈÙ™œÙ] ˆ™[ÝÈXXÚ][K\ÈH˜[YBˆ ËÈ[˜Ü™[Y[�ÈžHÛ™KˆÙHØ[ˆ[ˆY܈™[[Ý™Bˆ ËÈY[�H][\È\™H[™HÛˆØÜ™Y[ˆÜÚ][ÛˆÚ[ˆ ËÈZÙHØ\™HÙˆ]Ù[ˆÚ]Ý]\È™YY[™ÈÈY\܈ ËÈÚ]H�[˜ÚÙˆ\™ÛÛœÝ[�Ë‚‚ˆ ËÈ™\Ù]Y[�H][HÜÚ][ÛˆK‚ˆÜ×ÞHHQS•WÔÔ×ÖH -ÈQS•WÒUST×ÓPT‘ÒS—ÖN‚ˆÛY[�]^ - -Ù[XÝ܈OHUSWÔT‘“Ô“PS�ÑJKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ”\™›Ü›X[˜ÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÔT‘“Ô“PS�ÑJKÓÓSS—Ì—ÔÔ×Ö Ü×ÞK -Ø]™Y]K™X�YÚ[™›È ˆP•Q×ÑTÔVWÔT‘“Ô“PS�ÑHÈŠ‘[˜X›YŠHˆŠ‘\ØX›YŠJJNˆÜ×ÞJÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHUSWÔÔÒUSÓŠKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ�˜\ÚXÈ›Ü\�Y\ΈŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÔÔÒUSÓŠKÓÓSS—Ì—ÔÔ×Ö Ü×ÞK -Ø]™Y]K™X�YÚ[™›È ˆP•Q×ÑTÔVWÔ“ÔT•QTÈÈŠ‘[˜X›YŠHˆŠ‘\ØX›YŠJJNˆÜ×ÞJÊÎÈ‚ˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÐUPÒÊKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ�ÛÛ\Ú[Ûˆ]XÚΈŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÐUPÒÊKÓÓSS—Ì—ÔÔ×Ö Ü×ÞKÛÛ\Ú[Û—ÙX�Y×Ý\WÜÝš[™ÊØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSÓ—ÐUPÒ×Ì‘ P•Q×ÑTÔVWÐÓÓTÒSÓ—ÐUPÒ×ÌÑ -JNˆÜ×ÞJÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓГÑJKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ�ÛÛ\Ú[Ûˆ›ÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓГÑJKÓÓSS—Ì—ÔÔ×Ö Ü×ÞKÛÛ\Ú[Û—ÙX�Y×Ý\WÜÝš[™ÊØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSӗГÑWÌ‘ P•Q×ÑTÔVWÐÓÓTÒSӗГÑWÌÑ -JNˆÜ×ÞJÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÔÔPÑJKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ�ÛÛ\Ú[ÛˆÜXÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÔÔPÑJKÓÓSS—Ì—ÔÔ×Ö Ü×ÞKÛÛ\Ú[Û—ÙX�Y×Ý\WÜÝš[™ÊØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSÓ—ÔÔPÑWÌ‘ P•Q×ÑTÔVWÐÓÓTÒSÓ—ÔÔPÑWÌÑ -JNˆÜ×ÞJÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÔ�S‘ÑJKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠ”˜[™ÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHUSWÐÓÓÔ�S‘ÑJKÓÓSS—Ì—ÔÔ×Ö Ü×ÞK -Ø]™Y]K™X�YÚ[™›È ˆP•Q×ÑTÔVWÔ�S‘ÑHÈŠ‘[˜X›YŠHˆŠ‘\ØX›YŠJJNˆÜ×ÞJÊ΂ˆ ËÈ\Ü^H^]]BˆÛY[�]^J -Ù[XÝ܈OHUSWÑVU -KÜ×ÞH -È -QS•WÒUST×ÓPT‘ÒS—ÖJK Š�˜XÚÈŠJN‚ˆ ËÈ�[ˆ[ˆ[™Ú[™H\]K‚ˆ\]J -]™[OH•S -K -N‚ˆ ËÈYˆ\Ù\ˆ™\ÜÙ\È\ ÙÝÛˆ܈\ØË] ÜÈXÝXØÛÜ™[™ÛK‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘UT“Q×ÓSÕ‘QÕÓˆ“Q×ÑTÐÊJH‚ˆ ËÈYˆ\Ù\ˆ™\ÜÙ\È\ØØ\K[ˆÙ]]Z]ˆ ËÈ›YÈ[[YYX][Kˆ[ÙHÚ\ÙK[˜Ü™[Y[�ˆ ËÈ܈XÜ™[Y[�Ù[XÝ܈\È™YYY ‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊHˆ]Z]H NˆˆH[ÙHYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Hˆ ËÈ^H™Y\Yˆ]˜Z[X›K‚ˆ ËÈܘ]\È - LŒ ŒŠH[Ý™YH‘QTÛÝ[™ÈÛÜšÈÛ›HÚ]T ÑÕÓˆÙ^\ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -HˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ËÈYˆÙH\™H]HÜ][KÛ܈ ËÈÈ\Ý ˆÝ\�Ú\ÙK[Ý™HÛ™H\ ‚ˆYŠÙ[XÝ܈HQS•WÒUSWÑ’T”ÕÒS‘V -HˆÙ[XÝ܈HUSWÑVUˆH[ÙHˆ K\Ù[XÝ܎ˆB‚ˆH[ÙHYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠH‚ˆ ËÈ^H™Y\Yˆ]˜Z[X›K‚ˆ ËÈܘ]\È - LŒ ŒŠH[Ý™YH‘QTÛÝ[™ÈÛÜšÈÛ›HÚ]T ÑÕÓˆÙ^\ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -HˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ËÈYˆÙH\™H]H\Ý][Bˆ ËÈ -ÚXÚÚÝ[™H˜˜XÚÈŠK[‚ˆ ËÈÛܘXÚÈÈš\œÝ ˆÝ\�Ú\ÙBˆ ËÈ[Ý™HÛ™HÝÛ‹‚ˆYŠÙ[XÝ܈�HUSWÑVU -HˆÙ[XÝ܈HQS•WÒUSWÑ’T”ÕÒS‘VˆˆH[ÙHˆ -ÊÜÙ[XÝ܎ˆBˆBˆB‚‚ˆ ËÈÙÙÛHÙ[XÝ[Ûˆ˜[YHÛˆY� ÜšYÚÜ‚ˆ ËÈšYÙÙ\ˆ�]Ûˆ™\ÜË‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJHˆˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -HˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆÙÙÛWÙ\™XÝ[ÛˆH -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘SQ• -HÈÑÑÓWÑT‘PÕSÓ—Ð�PÒÕÐT‘ˆÑÑÓWÑT‘PÕSӗѓԕÐT‘ˆˆ ËÈ\È\ÈÚ\™HY[�H][\È\™H^XÝ]Y ‚ˆÝÚ]Ú -Ù[XÝÜŠHˆØ\ÙHUSWÔT‘“Ô“PS�ÑN‚ˆØ]™Y]K™X�YÚ[™›È�HP•Q×ÑTÔVWÔT‘“Ô“PS�ÑNˆœ™XZΈØ\ÙHUSWÔÔÒUSÓŽ‚ˆØ]™Y]K™X�YÚ[™›È�HP•Q×ÑTÔVWÔ“ÔT•QTΈœ™XZÎȈØ\ÙHUSWÐÓÓÐUPÒ΂ˆØ]™Y]K™X�YÚ[™›ÈHÛÛ\Ú[Û—ÙX�Y×Ý\WØÞXÛJØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSÓ—ÐUPÒ×Ì‘ P•Q×ÑTÔVWÐÓÓTÒSÓ—ÐUPÒ×ÌÑ ÙÙÛWÙ\™XÝ[ÛŠNˆœ™XZΈØ\ÙHUSWÐÓÓГÑN‚ˆØ]™Y]K™X�YÚ[™›ÈHÛÛ\Ú[Û—ÙX�Y×Ý\WØÞXÛJØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSӗГÑWÌ‘ P•Q×ÑTÔVWÐÓÓTÒSӗГÑWÌÑ ÙÙÛWÙ\™XÝ[ÛŠNˆœ™XZΈØ\ÙHUSWÐÓÓÔÔPÑN‚ˆØ]™Y]K™X�YÚ[™›ÈHÛÛ\Ú[Û—ÙX�Y×Ý\WØÞXÛJØ]™Y]K™X�YÚ[™›ËP•Q×ÑTÔVWÐÓÓTÒSÓ—ÔÔPÑWÌ‘ P•Q×ÑTÔVWÐÓÓTÒSÓ—ÔÔPÑWÌÑ ÙÙÛWÙ\™XÝ[ÛŠNˆœ™XZΈØ\ÙHUSWÐÓÓÔ�S‘ÑN‚ˆØ]™Y]K™X�YÚ[™›È�HP•Q×ÑTÔVWÔ�S‘ÑNˆœ™XZΈØ\ÙHUSWÑVU‚ˆ]Z]H NˆBˆBˆB‚ˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ‚ˆÝ[™YˆQS•WÔÔ×ÖBˆÝ[™YˆQS•WÒUST×ÓPT‘ÒS—ÖBˆÝ[™YˆÓÓSS—ÌWÔÔ×ÖˆÝ[™YˆÓÓSS—Ì—ÔÔ×ÖˆÝ[™YˆQS•WÒUSWÑ’T”ÕÒS‘VŸB‚‹Ê‚ŠˆØ\ÚÙ^K[[Ûˆ‹‚Šˆ Œ Œ‹L KL BŠˆŠˆ\Ü^HÚX]Ü[ÛœÈY[�H[™Šˆ\H^Y\ˆÙ[XÝ[ÛœË‚Š‹Â�›ÚYY[�WÛÜ[Ûœ×ØÚX]Ê -BžÂˆÙYš[™HQS•WÔÔ×ÖH MˆÙYš[™HQS•WÒUST×ÓPT‘ÒS—ÖH ‚ˆÙYš[™HÓÓSS—ÌWÔÔ×Ö LLBˆÙYš[™HÓÓSS—Ì—ÔÔ×ÖÓÓSS—ÌWÔÔ×Ö -È M‚ˆ\YYˆÝ�XÝ×ÛÜ[Û—Û\݈ˆWØÚX]ÛÜ[ÛœÈXÝ]™NˆWØÚX]ÛÜ[ÛœÈY[�NˆÚ\ˆX™[ÌŒNˆH×ÛÜ[Û—Û\Ý‚ˆ[�Ü[Û—Û\ÝØÛÝ[�H Έ×ÛÜ[Û—Û\ÝÜ[Û—Û\Ý×HHˆ˘XÝ]™HHÒPUÓÔSÓ”×ÒSTPÐP“WÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÒSTPÐP“WÓQS•K ›X™[H’[\XØX›HX\˜ÚˆˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÐÔ‘QU×ÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÐÔ‘QU×ÓQS•K ›X™[H’[™š[š]HÜ™Y]ΈˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÑS‘T‘ÖWÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÑS‘T‘ÖWÓQS•K ›X™[H’[™š[š]H[™\™ÞNˆˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÒPSÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÒPSÓQS•K ›X™[H’[™š[š]HX[ˆˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÓU‘T×ÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÓU‘T×ÓQS•K ›X™[H’[™š[š]H]™\ΈˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÓUSRUÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÓUSRUÓQS•K ›X™[H“][Z]Û]ÚˆˆKˆ˘XÝ]™HHÒPUÓÔSÓ”×ÕÑÐPÕU‘K ›Y[�HHÒPUÓÔSÓ”×ÕÑÓQS•K ›X™[H•ÝXÚÙˆX]ˆˆBˆNˆˆ[�Ü[Û—Û\ÝØÝ\œÛ܈H ˆ[�Ü[Û—Ø]˜Z[X›WØÛÝ[�H ˆ[�Ü[Û—Ø]˜Z[X›WÙš\œÝH ˆ[�Ü×ÞHH ˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆˆ›Ý™]ÚÙ^\ÈH ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÐÒPUÓÔSÓ”×ÓQS•NÈ‚ˆ ËÜš[�Š——ˆY[�WÛÜ[Ûœ×ØÚX]Ê -HŠN‚ˆÚ[H -\]Z] -Bˆˆ ʈ\Ü^HY[�H]Kˆ -‹ÂˆÛY[�]^J ‹QS•WÔÔ×ÖK Š�ÚX]Ü[ۜȊJNˆˆ Ê‚ˆ -ˆ™\Ù]Y[�H][HÜÚ][ÛˆH[™ˆ -ˆ˜[YY[�H][HÛÝ[� ‚ˆ -‹ÂˆÜ[Û—Ø]˜Z[X›WØÛÝ[�H ˆÜ[Û—Ø]˜Z[X›WÙš\œÝH ˆÜ×ÞHHQS•WÔÔ×ÖH -ÈQS•WÒUST×ÓPT‘ÒS—ÖN‚ˆ Ê‚ˆ -ˆ]\˜]H›ÝYÚHÜ[Ûˆ\݈ -ˆ\œ˜^H[™\Ü^H]˜Z[X›H][\Ë‚ˆ -ˆˆ -ˆ]XXÚ˜[YÜ[Û‹[˜Ü™[Y[�ˆ -ˆÜ[ÛˆÛÝ[�[™HÜÚ][Û‹‚ˆ -‹Â‚ˆ›Üˆ -Ü[Û—Û\ÝØÝ\œÛ܈H ÈÜ[Û—Û\ÝØÝ\œÛ܈Ü[Û—Û\ÝØÛÝ[�ÈÜ[Û—Û\ÝØÝ\œÛÜŠÊÊBˆÈˆYˆ -Ûؘ[ØÛÛ™šY˘ÚX]È ˆÜ[Û—Û\ÝÛÜ[Û—Û\ÝØÝ\œÛÜ—K›Y[�JBˆÈˆ ʈˆ -ˆYˆÜ[Ûˆ]˜Z[X›HÛÝ[�\ÈÝ[ \ˆ -ˆ\ÈHš\œÝ]˜Z[X›HÜ[Û‹ˆ™XÛÜ™Bˆ -ˆ[™^›Üˆ\ÙHÝۜݙX[K‚ˆ -‹Â‚ˆYˆ -[Ü[Û—Ø]˜Z[X›WØÛÝ[� -BˆˆÜ[Û—Ø]˜Z[X›WÙš\œÝHÜ[Û—Û\ÝØÝ\œÛ܎ˆBˆˆ ʈš[�HY[�H^[™Û‹ÛÙ™ˆÝ]\ˈ -‹ÂˆÛY[�]^ - -Ù[XÝ܈OHÜ[Û—Û\ÝØÝ\œÛÜŠKÓÓSS—ÌWÔÔ×Ö Ü×ÞKŠÜ[Û—Û\ÝÛÜ[Û—Û\ÝØÝ\œÛÜ—K›X™[ -JNˆÛY[�]^ - -Ù[XÝ܈OHÜ[Û—Û\ÝØÝ\œÛÜŠKÓÓSS—Ì—ÔÔ×Ö Ü×ÞK -Ûؘ[ØÛÛ™šY˘ÚX]È ˆÜ[Û—Û\ÝÛÜ[Û—Û\ÝØÝ\œÛÜ—K˜XÝ]™HÈŠ“ÛˆŠHˆŠ“Ù™ˆŠJJNˆˆÜ[Û—Ø]˜Z[X›WØÛÝ[� -ÊΈÜ×ÞJÊΈHˆB‚ˆ ʈ›È][\È]˜ZX[›Kˆ[\�^Y\‹ˆ -‹ÂˆYˆ -[Ü[Û—Ø]˜Z[X›WØÛÝ[� -BˆˆÛY[�]^J Ü×ÞK Š“›ÈÚX]È]˜Z[X›KˆŠJNˆH‚ˆ Ê‚ˆ -ˆYH^]Ü[Û‹ˆÙHØ[ˆ\ÙHH\œ˜^HÚ^™Hˆ -ˆ›ÜˆHÙ[XÝ܈[™^YHÈ [™^[™Ë‚ˆ -‹ÂˆˆÜ×ÞJÊÎȈÛY[�]^J -Ù[XÝ܈OHÜ[Û—Û\ÝØÛÝ[� -KÜ×ÞK Š�˜XÚÈŠJN‚ˆ ʈ�[ˆ[ˆ[™Ú[™H\]Kˆ -‹Âˆ\]J -]™[OH•S -K -Nˆˆ ʈˆ -ˆ[™H\Ù\ˆ[œ][™Ü[]HÙ[XÝ܈ˆ -ˆ\›ÜšX]H˜[YKˆˆ -ˆˆ -ˆš\œÝÙHÙ]Ù[XÝ܈Èš\œÝ][H[ˆØ\ÙHˆ -ˆHš\œÝ]˜Z[X›IÜÈ[™^\È›Ý ‚ˆ -ˆˆ -ˆ™^ [™H�]ۜˈTÐÈ]Z]ÈHY[�Hˆ -ˆ[œÝ[�Kˆ\ ÑÝÛˆÙ[XÝ\™Ù]][K‚ˆ -ˆY� ÔšYÚ ÐXÝ[Ûˆ�]ÛœÈÙÙÛH˜[YK‚ˆ -‹Â‚ˆYˆ -\Ù[XÝ܈ ‰ˆÜ[Û—Ø]˜Z[X›WÙš\œÝ -BˆˆÙ[XÝ܈HÜ[Û—Ø]˜Z[X›WÙš\œÝˆB‚ˆYˆ -›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆB‚ˆYˆ -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ ʈ^H™Y\Yˆ]˜Z[X›Kˆ -‹ÂˆYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ʈܘ\\›Ý[™Yˆ™YYY ˆ -‹ÂˆYˆ -Ù[XÝ܈HÜ[Û—Ø]˜Z[X›WÙš\œÝ -BˆˆÙ[XÝ܈HÜ[Û—Û\ÝØÛÝ[�ˆBˆ[ÙBˆˆÙ[XÝÜ‹KNˆB‚ˆ ʈÚÚ\\ØX›Y][\ˈ -‹ÂˆÚ[H -JÛؘ[ØÛÛ™šY˘ÚX]È ˆÜ[Û—Û\ÝÜÙ[XÝÜ—K›Y[�JH ‰ˆÙ[XÝ܈ˆÜ[Û—Ø]˜Z[X›WÙš\œÝ -BˆÈˆÙ[XÝÜ‹KNˆHˆBˆˆYˆ -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ ʈ^H™Y\Yˆ]˜Z[X›Kˆ -‹ÂˆYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ ʈܘ\\›Ý[™Yˆ™YYY ˆ -‹ÂˆYˆ -Ù[XÝ܈�HÜ[Û—Û\ÝØÛÝ[� -BˆˆÙ[XÝ܈HÜ[Û—Ø]˜Z[X›WÙš\œÝˆBˆ[ÙBˆˆÙ[XÝÜŠÊΈB‚ˆ ʈÚÚ\\ØX›Y][\ˈ -‹ÂˆÚ[H -JÛؘ[ØÛÛ™šY˘ÚX]È ˆÜ[Û—Û\ÝÜÙ[XÝÜ—K›Y[�JH ‰ˆÙ[XÝ܈Ü[Û—Û\ÝØÛÝ[� -BˆˆÙ[XÝÜŠÊΈBˆH‚ˆ ʈˆ -ˆÙÙÛHÙ[XÝ[Ûˆ˜[YHÛˆY� ÜšYÚÜ‚ˆ -ˆšYÙÙ\ˆ�]Ûˆ™\ÜË‚ˆ -‹ÂˆYˆ -›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆˆYˆ -Ûؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆ Ê‚ˆ -ˆZÙHXÝ[Ûˆ˜\ÙYÛˆÙ[XÝÜ‹‚ˆ -ˆˆ -ˆH\ÝÜ[Ûˆ\ÈH^]][KÛÈYˆ] ܈ -ˆÝ\ˆÙ[XÝ܈˜[YHÙH^]\ÈY[�Kˆˆ -‚ˆ -ˆÝ\�Ú\ÙH\ÙHÙ[XÝ܈\È\œ˜^HÙ^HÈÙ]Hˆ -ˆš]ÙHØ[�ÈÙÙÛH[ˆÝ\ˆ\™Ù]˜[YK‚ˆ -‹Â‚ˆYˆ -Ù[XÝ܈Ü[Û—Û\ÝØÛÝ[� -BˆˆÛؘ[ØÛÛ™šY˘ÚX]È�HÜ[Û—Û\ÝÜÙ[XÝÜ—K˜XÝ]™NˆBˆ[ÙBˆˆ]Z]H NˆHˆBˆBˆˆ ËÜØ]™\Ù][™ÜÊ -Nˆˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÐÒPUÓÔSÓ”×ÓQS•N‚ˆÝ[™YˆQS•WÔÔ×ÖBˆÝ[™YˆQS•WÒUST×ÓPT‘ÒS—ÖBˆÝ[™YˆÓÓSS—ÌWÔÔ×ÖˆÝ[™YˆÓÓSS—Ì—ÔÔ×ÖŸB‚�›ÚYY[�WÛÜ[Ûœ×ÜÞ\Ý[J -BžÂˆÙYš[™HÖT×ÓÔÖWÔÔÈ M‚ˆ[�[HˆÖT×ÓÔÓÑˈÖT×ÓÔÕ”ÑSPQÑKˆÖT×ÓÔÐÒPUˈÖT×ÓÔÑP•QˈÖT×ÓÔÐÓÓ‘’QˈÖT×ÓÔÐ�PÒˆN‚ˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�ÛÛ HH LLNˆ[�ÛÛ ˆHÛÛ JÌMˆ[�^ÛX™[ÈH ˆ[�‘UHÖT×ÓÔÐ�PÒΈ[�[™HH ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÔÖTÕSWÓÔSÓ”×ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ˆYˆ -›ÙX�YÛÜ[ÛœÊH^ÛX™[ÈH Nˆ‘U OH^ÛX™[ΈˆÚ[J\]Z] -Bˆˆ[™HH ‚ˆÛY[�]^J ‹ÖT×ÓÔÖWÔÔËL‹ Š”Þ\Ý[HÜ[ۜȊJN‚ˆÛY[�]^ - ÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ•Ý[�SNˆŠJNˆÛY[�]^ - ÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KЉ\ÈЈŠKÛÛ[X\š[� -Ù]Þ\Ý[T˜[JЖUTÊJJNˆ[™JÊΈˆÛY[�]^ - ÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ•\ÙY�SNˆŠJNˆÛY[�]^ - ÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KЉ\ÈЈŠKÛÛ[X\š[� -Ù]\ÙY˜[JЖUTÊJJNˆ[™JÊ΂ˆÛY[�]^ - ÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ“X^^Y\œÎˆŠJNˆÛY[�]^ - ÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KЉZHŠK]™[Ù]ÖØÝ\œ™[�ÜÙ]K›X^^Y\œÊNˆ[™JÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÓÑÊKÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ‘š[HÙÙÚ[™ÎˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÓÑÊKÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™K -Ø]™Y]K�\Ù[ÙÈÈŠ‘[˜X›YŠHˆŠ‘\ØX›YŠJJNˆ[™JÊ΂ˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJKÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ•™\œÝ\È[XYÙNˆŠK -NˆYŠ™\œÝ\Ù[XYÙHOH -BˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJKÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KŠ‘\ØX›YžH[Ù[HŠJNˆBˆ[ÙHYŠ™\œÝ\Ù[XYÙHOH JBˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJKÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KŠ‘[˜X›YžH[Ù[HŠJNˆBˆ[ÙBˆˆYŠØ]™Y]K›[ÙJBˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJKÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KŠ‘\ØX›YŠJNÈ ËÓ[ÙH H H^Y\œÈÐS‰Õ]XÚÈXXÚÝ\‚ˆBˆ[ÙBˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJKÛÛ ‹ÖT×ÓÔÖWÔÔÈ -È[™KŠ‘[˜X›YŠJNÈ ËÓ[ÙH ˆ H^Y\œÈÐSˆ]XÚÈXXÚÝ\‚ˆBˆBˆ[™JÊΈˆYˆ -Ûؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÓPTÕT—ÓQS•JBˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÐÒPUÊKÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ�ÚX]Ü[ۜˋ‹ˆŠJNˆ[™JÊΈB‚ˆYˆ -[›ÙX�YÛÜ[ÛœÊBˆˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÑP•QÊKÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ‘X�YÈÙ][™ÜË‹‹ˆŠJNˆ[™JÊΈB‚ˆÛY[�]^ - -Ù[XÝ܈OHÖT×ÓÔÐÓÓ‘’QÊKÛÛ KÖT×ÓÔÖWÔÔÈ -È[™KŠ�ÛÛ™šYÈÙ][™ÜË‹‹ˆŠJN‚ˆ ʈ^˜H[™\ț܈ÜXÚ[™Ëˆ -‹Âˆ[™H -ÏH ŽÂ‚ˆÛY[�]^J -Ù[XÝ܈OH‘U -KÖT×ÓÔÖWÔÔÈ -È[™K Š�˜XÚÈŠJN‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎‚ˆ ʈÚÚ\\ØX›Y][\ˈ -‹Â‚ˆYˆ -Ù[XÝ܈OHÖT×ÓÔÐÒPUÈ ‰ˆJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÓPTÕT—ÓQS•JJBˆˆ K\Ù[XÝ܎ˆB‚ˆYˆ -Ù[XÝ܈OHÖT×ÓÔÑP•QÈ ‰ˆ›ÙX�YÛÜ[ÛœÊBˆˆ K\Ù[XÝ܎ˆB‚ˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎‚ˆ ʈÚÚ\\ØX›Y][\ˈ -‹Â‚ˆYˆ -Ù[XÝ܈OHÖT×ÓÔÐÒPUÈ ‰ˆJÛؘ[ØÛÛ™šY˘ÚX]È ˆÒPUÓÔSÓ”×ÓPTÕT—ÓQS•JJBˆˆ -ÊÜÙ[XÝ܎ˆB‚ˆYˆ -Ù[XÝ܈OHÖT×ÓÔÑP•QÈ ‰ˆ›ÙX�YÛÜ[ÛœÊBˆˆ -ÊÜÙ[XÝ܎ˆH‚ˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H‘UˆBˆYŠÙ[XÝ܈ˆ‘U -BˆˆÙ[XÝ܈H ˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -N‚ˆYˆ -Ù[XÝ܈OH‘U -H]Z]H Nˆ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÓÑÊHØ]™Y]K�\Ù[ÙÈH\Ø]™Y]K�\Ù[ÙΈ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÕ”ÑSPQÑJBˆˆYˆ -™\œÝ\Ù[XYÙHˆ JBˆˆYˆ -Ø]™Y]K›[ÙJBˆˆØ]™Y]K›[ÙHH ˆBˆ[ÙBˆˆØ]™Y]K›[ÙHH NˆBˆBˆBˆ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÐÒPUÊBˆˆY[�WÛÜ[Ûœ×ØÚX]Ê -NˆBˆ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÑP•QÈ ‰ˆ[›ÙX�YÛÜ[ÛœÊBˆˆY[�WÛÜ[Ûœ×ÙX�YÊ -NˆBˆ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÐÒPUÊBˆˆY[�WÛÜ[Ûœ×ØÚX]Ê -NˆBˆ[ÙHYˆ -Ù[XÝ܈OHÖT×ÓÔÑP•QÈ ‰ˆ[›ÙX�YÛÜ[ÛœÊHY[�WÛÜ[Ûœ×ÙX�YÊ -Nˆ[ÙHYˆ -Ù[XÝÜ�OTÖT×ÓÔÐÓÓ‘’QÊHY[�WÛÜ[Ûœ×ØÛÛ™šYÊ -Nˆ[ÙH]Z]H NˆBˆBˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÔÖTÕSWÓÔSÓ”×ÓQS•N‚ˆÝ[™YˆÖT×ÓÔÖWÔÔŸB‚‚�›ÚYY[�WÛÜ[Ûœ×ÝšY[Ê -BžÂˆ[�]Z]H ˆ[�Ù[XÝ܈H ˆ[�\ŽÂˆ[�ÛÛ HH LMKÛÛ ˆH N‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—Õ’QS×ÓÔSÓ”×ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ‚ˆÚ[J\]Z] -BˆˆÛY[�]^J ‹ MK Š•šY[ÈÜ[ۜȊJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K LËŠ�œšYÚ™\ÜΈŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ LˉZH‹Ø]™Y]K˜œšYÚ™\ÜÊNˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ K L‹Š‘Ø[[XNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ ‹ L‹‰ZH‹Ø]™Y]K™Ø[[XJNˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ K LKŠ•Ú[™ÝÈÙ™œÙ]ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ ‹ LK‰ZH‹Ø]™Y]K�Ú[™ÝÜÜÊN‚ˆÚYˆÑˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ K Š‘\Ü^H[ÙNˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ ‹ Ø]™Y]K™�[ØÜ™Y[ˆÈŠ‘�[ŠHˆŠ•Ú[™ÝÈŠJN‚ˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K KŠ•šY[ȘXÚÙ[™ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ K -Ü[™ÛÈŠ“Ü[‘ÓŠHˆŠ”ÑŠJJN‚ˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ K ‹Š”ØØ[NˆŠJNˆÚY™YˆS‘“ÒQˆYŠØ]™Y]KšÜØØ[HOH -BˆÙ[ÙB‚BZYŠØ]™Y]K™�[ØÜ™Y[ŠBˆÙ[™Y‚ˆˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ ‹ ‹Š�]]ÛX]XÈŠJNˆBˆ[ÙBˆˆÛY[�]^ - -Ù[XÝ܈OH JKÛÛ ‹ ‹‰M Œ™ž H Z^ ZH‹Ø]™Y]KšÜØØ[K -[� -JšY[Û[Ù\Ëš™\È -ˆØ]™Y]KšÜØØ[JK -[� -JšY[Û[Ù\Ë�”™\È -ˆØ]™Y]KšÜØØ[JJNˆB‚ˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ K ËŠ’\™Ø\™Hš[\ŽˆŠJNˆˆÚ\ˆ -™š[\“˜[YNˆYˆ -Ø]™Y]KšÜØØ[HOH KŒ ‰ˆ\Ø]™Y]K™�[ØÜ™Y[ŠBˆš[\“˜[YHH‘\ØX›YŽÂˆ[ÙHYˆ -Ü[™Û -Bˆš[\“˜[YHH’YÚ]X[]HŽÂˆ[ÙHYˆ -Ø]™Y]KšÙš[\ŠBˆš[\“˜[YHH”Ú[\HŽÂˆ[ÙBˆš[\“˜[YHH�š[[™X\ˆŽÂˆÛY[�]^ - -Ù[XÝ܈OH ŠKÛÛ ‹ ËŠš[\“˜[YJJNˆB‚ˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ K Š”ÛÙ�Ø\™Hš[\ŽˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH ÊKÛÛ ‹  - -Ø]™Y]KšÜØØ[H�H ‹ŒØ]™Y]K™�[ØÜ™Y[ŠHÈŠÙž›]\“˜[Y\ÖÜØ]™Y]KœÝÙš[\—JHˆŠ‘\ØX›YŠJJN‚ˆÚ\ˆœÛ[Z]Ý^ÌÌ—NˆÝÚ]Ú -Ø]™Y]K™œÛ[Z] -BˆˆØ\ÙH ‚ˆÛœš[�ŠœÛ[Z]Ý^ Ú^™[ÙŠœÛ[Z]Ý^ -K‰\È‹Š‘\ØX›YŠJNˆœ™XZΈØ\ÙH N‚ˆÛœš[�ŠœÛ[Z]Ý^ Ú^™[ÙŠœÛ[Z]Ý^ -K‰ZH -”Þ[˜ÊH‹šY[רÝ\œ™[�Ü™Yœ™\Úܘ]J -JNˆœ™XZΈØ\ÙH Ž‚ˆÛœš[�ŠœÛ[Z]Ý^ Ú^™[ÙŠœÛ[Z]Ý^ -K‰ZH‹ Œ -Nˆœ™XZΈØ\ÙH ΂ˆÛœš[�ŠœÛ[Z]Ý^ Ú^™[ÙŠœÛ[Z]Ý^ -K‰ZH‹ L -Nˆœ™XZΈY˜][‚ˆÛœš[�ŠœÛ[Z]Ý^ Ú^™[ÙŠœÛ[Z]Ý^ -K‰\È‹Š•[šÛ›ÝÛˆŠJNˆœ™XZΈB‚ˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ K KŠ‘”È[Z]ˆŠJNˆÛY[�]^ - -Ù[XÝ܈OH -KÛÛ ‹ KœÛ[Z]Ý^ -N‚ˆYŠØ]™Y]K™�[ØÜ™Y[ŠBˆˆÛY[�]^ - -Ù[XÝ܈OHJKÛÛ K ‹Š‘�[ØÜ™Y[ˆ\NˆŠJNˆÛY[�]^ - -Ù[XÝ܈OHJKÛÛ ‹ ‹ -Ø]™Y]KœÝ™]ÚÈŠ”Ý™]ÚÈØÜ™Y[ˆŠHˆŠ”™\Ù\�™H\ÜXݘ][ÈŠJJNˆBˆ[ÙHYŠÙ[XÝ܈OHJBˆˆÙ[XÝ܈H -›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -HȈ LˆB‚ˆÛY[�]^J -Ù[XÝ܈OH L -KK Š�˜XÚÈŠJNˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H LˆBˆYŠÙ[XÝ܈ˆ L -BˆˆÙ[XÝ܈H ˆBˆÙ[™Y‚‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆˆ\ˆH ‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘SQ• -Bˆˆ\ˆH LNˆBˆ[ÙHYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘T’QÒ -Bˆˆ\ˆH NˆB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆØ]™Y]K˜œšYÚ™\ÜÈ -ÏH -ˆ\ŽÂˆYŠØ]™Y]K˜œšYÚ™\ÜÈ L�MŠBˆˆØ]™Y]K˜œšYÚ™\ÜÈH L�MŽÂˆBˆYŠØ]™Y]K˜œšYÚ™\ÜȈ �MŠBˆˆØ]™Y]K˜œšYÚ™\ÜÈH �MŽÂˆBˆ™ØWÝ�ØZ] - -NˆÙ]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠØ]™Y]K™Ø[[XKØ]™Y]K˜œšYÚ™\ÜÊNˆœ™XZΈØ\ÙH N‚ˆØ]™Y]K™Ø[[XH -ÏH -ˆ\ŽÂˆYŠØ]™Y]K™Ø[[XH L�MŠBˆˆØ]™Y]K™Ø[[XHH L�MŽÂˆBˆYŠØ]™Y]K™Ø[[XHˆ �MŠBˆˆØ]™Y]K™Ø[[XHH �MŽÂˆBˆ™ØWÝ�ØZ] - -NˆÙ]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠØ]™Y]K™Ø[[XKØ]™Y]K˜œšYÚ™\ÜÊNˆœ™XZΈØ\ÙH Ž‚ˆØ]™Y]K�Ú[™ÝÜÜÈ -ÏH\ŽÂˆYŠØ]™Y]K�Ú[™ÝÜÜÈ LŠBˆˆØ]™Y]K�Ú[™ÝÜÜÈH LŽÂˆBˆYŠØ]™Y]K�Ú[™ÝÜÜȈ Œ -BˆˆØ]™Y]K�Ú[™ÝÜÜÈH ŒˆBˆœ™XZΈÚYˆÑˆØ\ÙH ΂ˆšY[×Ù�[ØÜ™Y[—Ù›\ - -Nˆœ™XZΈØ\ÙH ‚ˆØ]™Y]K�\ÙYÛH\Ø]™Y]K�\ÙYÛˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆÙ]ØÛÛÜ—ØÛÜœ™XÝ[ÛŠØ]™Y]K™Ø[[XKØ]™Y]K˜œšYÚ™\ÜÊNˆœ™XZΈØ\ÙH N‚ˆÚY›™YˆS‘“ÒQˆYŠØ]™Y]K™�[ØÜ™Y[ŠBˆˆœ™XZΈBˆØ]™Y]KšÜØØ[H -ÏH\ˆ -ˆ Œ�NˆYŠØ]™Y]KšÜØØ[H Œ�JBˆˆØ]™Y]KšÜØØ[HH Œ�NˆBˆYŠØ]™Y]KšÜØØ[Hˆ Œ -BˆˆØ]™Y]KšÜØØ[HH Œ ˆBˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆÙ[ÙBˆØ]™Y]KšÜØØ[H -ÏH\ˆ -ˆ Œ�NˆYŠØ]™Y]KšÜØØ[H Œ -BˆˆØ]™Y]KšÜØØ[HH ŒˆBˆYŠØ]™Y]KšÜØØ[Hˆ Œ -BˆˆØ]™Y]KšÜØØ[HH Œ ˆBˆÙ[™Y‚ˆØ\ÙH Ž‚ˆÚY›™YˆS‘“ÒQˆYˆ -Ü[™Û -\Ø]™Y]K™�[ØÜ™Y[ˆ ‰ˆØ]™Y]KšÜØØ[HOH KŒ -JBˆˆœ™XZΈBˆÙ[™Y‚ˆØ]™Y]KšÙš[\ˆ -ÏH\ŽÂˆYŠØ]™Y]KšÙš[\ˆ -BˆˆØ]™Y]KšÙš[\ˆH NˆBˆYŠØ]™Y]KšÙš[\ˆˆ JBˆˆØ]™Y]KšÙš[\ˆH ˆBˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊN‚BBBXœ™XZΈØ\ÙH ΂ˆYŠ\Ø]™Y]K™�[ØÜ™Y[ˆ ‰ˆØ]™Y]KšÜØØ[H ‹Œ -Bˆˆœ™XZΈBˆšY[Û[Ù\Ë™š[\ˆ -ÏH\ŽÂˆYŠšY[Û[Ù\Ë™š[\ˆˆ“UT—ÓPV H JBˆˆšY[Û[Ù\Ë™š[\ˆH ˆBˆYŠšY[Û[Ù\Ë™š[\ˆ -BˆˆšY[Û[Ù\Ë™š[\ˆH“UT—ÓPV H NˆBˆØ]™Y]KœÝÙš[\ˆHšY[Û[Ù\Ë™š[\ŽÂˆY[\Ù] -[P�Y™™\‹   L� MŒ -N‚BBB]šY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆœ™XZΈØ\ÙH‚ˆØ]™Y]K™œÛ[Z]H -Ø]™Y]K™œÛ[Z] -È\ŠH H ˆYˆ -Ø]™Y]K™œÛ[Z] -HØ]™Y]K™œÛ[Z] -ÏH ˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆœ™XZΈØ\ÙHN‚ˆšY[×ÜÝ™]Ú - -Ø]™Y]KœÝ™]Ú�H JJNˆœ™XZΈÙ[™Y‚ˆY˜][‚ˆ]Z]H NˆBˆBˆBˆØ]™\Ù][™ÜÊ -Nˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—Õ’QS×ÓÔSÓ”×ÓQS•NŸB‚‚�›ÚYY[�WÛÜ[ÛœÊ -BžÂˆÙYš[™HÔÖWÔÔÈ LBˆÙYš[™HÔÖÔÔÈ Mˆˆ\YYˆ[�[Hˆ’QS×ÓÔSÓ‹ˆÓÕS‘ÓÔSÓ‹ˆÓÓ•“ÓÓÔSÓ‹ˆÖTÕSWÓÔSÓ‹‚ˆS‘ÓÔSÓ‚ˆHWÜÙ[XÝ܎‚ˆ[�]Z]H ˆ[�WÛÙ™œÙ]HÔÖWÔÔΈ[��PÒ×ÓÔSÓˆHS‘ÓÔSӎˆWÜÙ[XÝ܈Ù[XÝ܈H’QS×ÓÔSӎ‚ˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÓÔSÓ”×ÓQS•Nˆ›Ý™]ÚÙ^\ÈH ȈˆÚ[J\]Z] -BˆÈˆÛY[�]^J -Ù[XÝ܈OH’QS×ÓÔSÓŠKWÛÙ™œÙ] -Õ’QS×ÓÔSÓ‹ Š•šY[ÈÜ[ۜˋ‹ˆŠJNˆÛY[�]^J -Ù[XÝ܈OHÓÕS‘ÓÔSÓŠKWÛÙ™œÙ] -ÔÓÕS‘ÓÔSÓ‹ Š”ÛÝ[™Ü[ۜˋ‹ˆŠJNˆÛY[�]^J -Ù[XÝ܈OHÓÓ•“ÓÓÔSÓŠKWÛÙ™œÙ] -ÐÓÓ•“ÓÓÔSÓ‹ Š�ÛÛ�›ÛÜ[ۜˋ‹ˆŠJNˆÛY[�]^J -Ù[XÝ܈OHÖTÕSWÓÔSÓŠKWÛÙ™œÙ] -ÔÖTÕSWÓÔSÓ‹ Š”Þ\Ý[HÜ[ۜˋ‹ˆŠJNˆˆÛY[�]^J -Ù[XÝ܈OH�PÒ×ÓÔSÓŠKWÛÙ™œÙ] -Ð�PÒ×ÓÔSÓŠÌ‹ Š�˜XÚÈŠJN‚ˆ\]J -]™[OH•S -K -N‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -BˆˆYŠÙ[XÝ܈H’QS×ÓÔSÓŠBˆˆÙ[XÝ܈H�PÒ×ÓÔSӎˆBˆ[ÙH K\Ù[XÝ܎‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÙ[XÝ܈ˆ�PÒ×ÓÔSÓŠBˆˆÙ[XÝ܈H’QS×ÓÔSӎˆB‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÓSÕ‘SQ•“Q×ÓSÕ‘T’QÒ“Q×ÐS–P•UÓŠJBˆ‚ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆB‚ˆYŠÙ[XÝÜ�OP�PÒ×ÓÔSÓŠH]Z]H Nˆ[ÙHYŠÙ[XÝÜ�OU’QS×ÓÔSÓŠHY[�WÛÜ[Ûœ×ÝšY[Ê -Nˆ[ÙHYŠÙ[XÝÜ�OTÓÕS‘ÓÔSÓŠHY[�WÛÜ[Ûœ×ÜÛÝ[™ - -Nˆ[ÙHYŠÙ[XÝÜ�OPÓÓ•“ÓÓÔSÓŠHY[�WÛÜ[Ûœ×Ú[œ] - -Nˆ[ÙHYŠÙ[XÝÜ�OTÖTÕSWÓÔSÓŠBˆˆY[�WÛÜ[Ûœ×ÜÞ\Ý[J -NȈBˆˆ[ÙH]Z]H NˆBˆBˆØ]™\Ù][™ÜÊ -NˆYŠÜ]\ÙHOH JBˆˆÜ]\ÙHH ŽÂˆBˆ›Ý™]ÚÙ^\ÈH ˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÓÔSÓ”×ÓQS•N‚ˆÝ[™YˆÕÐÒPUˆÝ[™YˆÔÖWÔÔˆÝ[™YˆÔÖÔÔˆÝ[™YˆÒPUÔUTÑWÔÔÖBŸB‚‹ËÈ KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKB‚‚�›ÚYÜ[˜›Ü“XZ[Š[�\™ØËÚ\ˆ -Š˜\™ÝŠBžÂˆÜš]WÛX\H•Sˆ[�]Z]H ˆ[�™[˜XÚÈH Nˆ[�Ù[XÝ܈H ˆL̈[�›Ý[YHH ˆ[�Ý\�YH ˆÚ\ˆ\�Y™–ÓPVЕQ‘‘T—ÓS—HHȈŸNˆ[�^Y\œÖÓPVÔVQT”×HHÌ    Nˆ[�Nˆ[�\™Û‚ˆš[�Š“Ü[�›Ôˆ \ËÛÛ\[H]Nˆˆ×ÑUW×È——ˆ‹‘T”ÒSÓŠN‚ˆYŠ\™ØÈˆ JBˆˆ\™ÛHÝ›[Š\™Ý–ÌWJNˆYŠ\™Ûˆ M ‰ˆ[Y[XÛ\ -\™Ý–ÌWK›Ù™œØÜ™Y[šÚ[H‹ M -JBˆˆQ�USÓÑ‘”ÐÔ‘QS—ÒÒSHÙ]˜[Y[� - -Ú\ˆ -ŠX\™Ý–ÌWH -È M ˆ‹ˆŠNˆBˆYŠ\™Ûˆ M ‰ˆ[Y[XÛ\ -\™Ý–ÌWKœÚÝÙš[\Ý\ÙYH‹ M -JBˆˆš[�š[U\ØYÙTÝ]\ÝXÜÈHÙ]˜[Y[� - -Ú\ˆ -ŠX\™Ý–ÌWH -È M ˆ‹ˆŠNˆBˆB‚‚ˆ[Ù[ÛY\ÝHÜ™X]S[Ù[ÛÛ[X[™\Ý - -Nˆ[Ù[ÝÛY\ÝHÜ™X]S[Ù[ÝÛÛ[X[™\Ý - -Nˆ]™[ÛY\ÝHÜ™X]S]™[ÛÛ[X[™\Ý - -Nˆ]™[Ü™\˜ÛY\ÝHÜ™X]S]™[Ü™\�ÛÛ[X[™\Ý - -NˆÜ™X]S[Ù[\Ý - -N‚ˆ ËÈØY™XÙ\ÜØ\žHÛÛ\Û™[�Ë‚ˆš[�Š‘Ø[YHÙ[XÝYˆ \×—ˆ‹XÚÙš[JNˆØYÙ][™ÜÊ -NˆÝ\�\ - -N‚X›Ý™]ÚÙ^\ÈH ‚ˆYŠÚÚ\ÜÙ] -Bˆ‚ˆ ËÈ™]È[\›˜]]™H˜XÚÙÜ›Ý[™] ‚ˆYŠÝ\ÝšÙÜ™ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝšÙÜ™ÊNˆÝ˜Ø] -\�Y™‹›ÙÛÈŠNˆØYؘXÚÙÜ›Ý[™ -\�Y™ŠNˆBˆ[ÙBˆˆØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÛÙÛÈŠNˆB‚ˆÚ[JÝ[YHÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ˆ ‰ˆJ›Ý™]ÚÙ^\È ˆ -“Q×ÐS–P•UÓˆ“Q×ÑTÐÊJJBˆˆ\]J  -NˆB‚ˆ]\ÚXÊ™]KÛ]\ÚXËÜ™[Z^‹ K -N‚ˆ ËÈ™]È[\›˜]]™HØÙ[™H] ‚ˆYŠÝ\ÝØÙ[™\ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝØÙ[™\ÊNˆÝ˜Ø] -\�Y™‹›ÙÛË�ŠNˆ^\ØÙ[™J\�Y™ŠNˆBˆ[ÙBˆˆ^\ØÙ[™J™]KÜØÙ[™\ËÛÙÛË�ŠNˆBˆBˆÛX\œØÜ™Y[Š˜XÚÙÜ›Ý[™ -N‚ˆÚ[J\]Z] -BˆˆYŠÚÚ\ÜÙ]  ‰ˆJÛÝ×ÛXZ[›Y[�WÙ›YÉŽ -JBˆˆYŠÝ[YH�H[�›Ý[YJBˆˆ ËÈ™]È[\›˜]]™HØÙ[™H] ‚ˆYŠÝ\ÝØÙ[™\ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝØÙ[™\ÊNˆÝ˜Ø] -\�Y™‹š[�›Ë�ŠNˆ^\ØÙ[™J\�Y™ŠNˆBˆ[ÙBˆˆ^\ØÙ[™J™]KÜØÙ[™\ËÚ[�›Ë�ŠNˆBˆ\]J  -Nˆ[�›Ý[YHHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ Œˆ™[˜XÚÈH NˆÝ\�YH ˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÑTÐÊBˆˆ]Z]H NˆBˆBˆ[ÙBˆˆÝ\�YH Nˆ™[˜XÚÈH ˆB‚ˆYˆ -ÛÝ×ÛXZ[›Y[�WÙ›YÈOH -HÛÝ×ÛXZ[›Y[�WÙ›YÈH ˆYŠ\Ý\�Y -BˆˆYŠ -Ý[YH HÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -H -Ûؘ[ØÛÛ™šYË™Ø[YWÜÜYY È ŠJBˆˆÛY[�]^J   Š”‘TÔÈÕT•ŠJNˆBˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÐS–P•UÓŠJBˆˆÝ\�YH Nˆ™[˜XÚÈH NˆBˆBˆ[ÙHYŠÚÚ\ÜÙ]�H -BˆˆØYØ[YQš[J -Nˆ^YØ[YJ^Y\œË\ÙTÙ]�H È\ÙTÙ]ˆÚÚ\ÜÙ] \ÙTØ]™JNˆBˆ[ÙBˆˆÛY[�]^J -Ù[XÝ܈OH -K ‹ Š”Ý\�Ø[YHŠJNˆÛY[�]^J -Ù[XÝ܈OH JK Ë Š“Ü[ۜȊJNˆÛY[�]^J -Ù[XÝ܈OH ŠK  Š’ÝÈÈ^HŠJNˆÛY[�]^J -Ù[XÝ܈OH ÊK K Š’[Ùˆ˜[YHŠJNˆÛY[�]^J -Ù[XÝ܈OH -K ‹ Š”]Z]ŠJNˆYŠÙ[XÝ܈ -BˆˆÙ[XÝ܈H ˆBˆYŠÙ[XÝ܈ˆ -BˆˆÙ[XÝ܈H ˆB‚ˆYŠ›Ý™]ÚÙ^\ÊBˆˆ[�›Ý[YHHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ŒˆB‚ˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘UT -Bˆˆ K\Ù[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ“Q×ÓSÕ‘QÕÓŠBˆˆ -ÊÜÙ[XÝ܎ˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\  Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆBˆYŠ›Ý™]ÚÙ^\È ˆ -“Q×ÐS–P•UÓŠJBˆˆYŠÛؘ[ÜØ[\WÛ\Ý ˜™Y\̈�H -BˆˆÛÝ[™Ü^WÜØ[\JÛؘ[ÜØ[\WÛ\Ý ˜™Y\Ì‹ Ø]™Y]K™Y™™XÝ›Û Ø]™Y]K™Y™™XÝ›Û  L -NˆBˆÝÚ]Ú -Ù[XÝÜŠBˆˆØ\ÙH ‚ˆ›ÜŠHH ÈHPVÔVQT”ÎÈJÊÊBˆˆ^Y\œÖÚWHH^Y\–ÚWK›™]ÚÙ^\È ˆ -“Q×ÐS–P•UÓŠNˆBˆ™[˜XÚÈHÚÛÜÙWÛ[ÙJ^Y\œÊNˆYŠ™[˜XÚÊBˆˆÝ\�YH ˆBˆœ™XZΈØ\ÙH N‚ˆY[�WÛÜ[ÛœÊ -Nˆœ™XZΈØ\ÙH Ž‚ˆˆ[�™]š[Ý\ÓÛÜH]\ÚXÛÛ܈Ú\ˆ™]š[Ý\Ó]\ÚXÖÜÚ^™[ÙŠÝ\œ™[�]\ÚXÊWN‚ˆY[\Ù] -™]š[Ý\Ó]\ÚXË Ú^™[ÙŠ™]š[Ý\Ó]\ÚXÊJNˆÝ˜ÜJ™]š[Ý\Ó]\ÚXËÝ\œ™[�]\ÚXÊN‚ˆYŠÝ\ÝØÙ[™\ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝØÙ[™\ÊNˆÝ˜Ø] -\�Y™‹šÝÝË�ŠNˆ^\ØÙ[™J\�Y™ŠNˆBˆ[ÙBˆˆ^\ØÙ[™J™]KÜØÙ[™\ËÚÝÝË�ŠNˆBˆYŠÝšXÛ\ -™]š[Ý\Ó]\ÚXËÝ\œ™[�]\ÚXÊHOH -Bˆˆ]\ÚXÊ™]š[Ý\Ó]\ÚXË™]š[Ý\ÓÛÜ  -NˆBˆ™[˜XÚÈH Nˆœ™XZΈBˆØ\ÙH ΂ˆ[˜[YJ -Nˆ™[˜XÚÈH Nˆœ™XZΈY˜][‚ˆ]Z]H Nˆœ™XZΈBˆ[�›Ý[YHHÝ[YH -ÈÛؘ[ØÛÛ™šYË™Ø[YWÜÜYY -ˆ ŒˆBˆBˆYŠ™[˜XÚÊBˆˆYŠÝ\�Y -Bˆˆ ʈˆܘ]\È - L LŒ ŒJHYY[ˆY][Û˜[[œÝ[˜ÙHÙˆH˜[œÛ][Ûˆ�[˜Ý[Ûˆ]Y[�HØÜ™Y[‚ˆ\ÙYÈ™Yœ™\Ú[^Ú]Ý]ÛÜÙH[™™[Ü[ˆH[™Ú[™B‚ˆܘ]\È - ‹LŒ ŒÊHYY[ˆY][Û˜[[œÝ[˜ÙHÙˆHšY[×Û[ÙH�[˜Ý[Ûˆ]Y[�HØÜ™Y[‚ˆ\ÙYÈ™Yœ™\ÚHÝXÚØÜ™Y[ˆ›Ü\�Y\ÈÛˆ[™›ÚYÚ]Ý]™\Ý\�[™Âˆ -‹ÂˆšY[×ÜÙ]Û[ÙJšY[Û[Ù\ÊNˆØ—Ú[š]˜[œÊ -NˆˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÓQS•NˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÕUN‚ˆYŠÝ\ÝšÙÜ™ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝšÙÜ™ÊNˆÝ˜Ø] -\�Y™‹�]XˆŠNˆØYؘXÚÙÜ›Ý[™ -\�Y™ŠNˆBˆ[ÙBˆˆØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÝ]XˆŠNˆBˆBˆ[ÙBˆˆØÜ™Y[—ÜÝ]\È �H’S—ÔÐÔ‘QS—ÓQS•NˆØÜ™Y[—ÜÝ]\ÈHS—ÔÐÔ‘QS—ÕUN‚ˆYŠÝ\ÝšÙÜ™ÈOH•S -BˆˆÝ˜ÜJ\�Y™‹Ý\ÝšÙÜ™ÊNˆÝ˜Ø] -\�Y™‹�]HŠNˆØYؘXÚÙÜ›Ý[™ -\�Y™ŠNˆBˆ[ÙBˆˆØYØØXÚYؘXÚÙÜ›Ý[™ -™]KØ™ÜËÝ]HŠNˆBˆB‚ˆYŠ\ÛÝ[™Ü]Y\žWÛ]\ÚXÊ•S •S -JBˆˆ]\ÚXÊ™]KÛ]\ÚXËÜ™[Z^‹ K -NˆBˆ™[˜XÚÈH ˆBˆ\]J  -NˆBˆ›Ü”Ú]ÝÛŠ Q�USÔÒUÕÓ—ÓQTÔÐQÑJNŸB‚ˆÝ[™YˆÑUÐT‘ˆÝ[™YˆÑUÐT‘×ÓS‚ˆÝ[™YˆÑUÐT‘ÔˆÝ[™YˆÑUÐT‘ÔÓS‚ˆÝ[™YˆÑUÒS•ÐT‘ˆÝ[™YˆÑUÑ“ÐUÐT‘ˆÝ[™YˆÑUÒS•ÐT‘ÔˆÝ[™YˆÑUÑ“ÐUÐT‘Ô \ No newline at end of file + newchar->move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + } + else if(stricmp(value, "panel") == 0) // NPC type + { + newchar->type = TYPE_PANEL; + newchar->antigravity = 1.0; //float type + newchar->move_config_flags |= (MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_GRAVITY); + } + else + { + borShutdown(1, "Model '%s' has invalid type: '%s'", filename, value); + } +} + +void lcmHandleCommandSubtype(ArgList *arglist, s_model *newchar, char *filename) +{ + char *value = GET_ARGP(1); + int i; + if(stricmp(value, "biker") == 0) + { + newchar->subtype = SUBTYPE_BIKER; + if(newchar->aimove == AIMOVE1_NONE) + { + newchar->aimove = AIMOVE1_NORMAL; + } + newchar->aimove |= AIMOVE1_BIKER; + if(!newchar->offscreenkill) + { + newchar->offscreenkill = 300; + } + + /* Bikers deault to taking double damage. */ + for(i = 0; i < max_attack_types; i++) + { + newchar->defense[i].factor = 2.f; + } + + newchar->move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_MAX_Z | MOVE_CONFIG_SUBJECT_TO_MIN_Z); + newchar->move_config_flags &= ~(MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_SCREEN | MOVE_CONFIG_SUBJECT_TO_WALL); + } + else if(stricmp(value, "arrow") == 0) // 7-1-2005 Arrow type + { + newchar->subtype = SUBTYPE_ARROW; // 7-1-2005 Arrow type + if(newchar->aimove == AIMOVE1_NONE) + { + newchar->aimove = AIMOVE1_NORMAL; + } + newchar->aimove |= AIMOVE1_ARROW; + if(!newchar->offscreenkill) + { + newchar->offscreenkill = 200; + } + + newchar->move_config_flags |= (MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_MAX_Z | MOVE_CONFIG_SUBJECT_TO_MIN_Z); + newchar->move_config_flags &= ~(MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_SCREEN | MOVE_CONFIG_SUBJECT_TO_WALL); + } + else if(stricmp(value, "notgrab") == 0) + { + newchar->subtype = SUBTYPE_NOTGRAB; + } + // ltb 1-18-05 Item Subtype + else if(stricmp(value, "touch") == 0) + { + newchar->subtype = SUBTYPE_TOUCH; + } + else if(stricmp(value, "weapon") == 0) + { + newchar->subtype = SUBTYPE_WEAPON; + } + else if(stricmp(value, "noskip") == 0) // Text animation cannot be skipped if subtype noskip + { + newchar->subtype = SUBTYPE_NOSKIP; + } + else if(stricmp(value, "flydie") == 0) // Obstacle will fly across the screen when hit if subtype flydie + { + newchar->subtype = SUBTYPE_FLYDIE; + } + else if(stricmp(value, "both") == 0) + { + newchar->subtype = SUBTYPE_BOTH; + } + else if(stricmp(value, "project") == 0) + { + newchar->subtype = SUBTYPE_PROJECTILE; + } + else if(stricmp(value, "follow") == 0) + { + newchar->subtype = SUBTYPE_FOLLOW; + child_follow_getsert_property(&newchar->child_follow); + } + else if(stricmp(value, "chase") == 0) + { + newchar->subtype = SUBTYPE_CHASE; + } + // end new subtype + else + { + borShutdown(1, "Model '%s' has invalid subtype: '%s'", filename, value); + } +} + +void lcmHandleCommandSmartbomb(ArgList *arglist, s_model *newchar, char *filename) +{ + //smartbomb now use a normal attack box + if(!newchar->smartbomb) + { + newchar->smartbomb = attack_allocate_object(); + } + else + { + borShutdown(1, "Model '%s' has multiple smartbomb commands defined.", filename); + } + + newchar->smartbomb->attack_force = atoi(GET_ARGP(1)); // Special force + newchar->smartbomb->attack_type = atoi(GET_ARGP(2)); // Special attack type + newchar->smartbomb->attack_drop = 1; //by default + newchar->smartbomb->dropv.y = default_model_dropv.y; + + if(newchar->smartbomb->attack_type == ATK_BLAST) + { + newchar->smartbomb->blast = 1; + newchar->smartbomb->dropv.x = default_model_dropv.x * 2.083f; + } + else + { + newchar->smartbomb->dropv.x = default_model_dropv.x; + } + + if(newchar->smartbomb->attack_type == ATK_FREEZE) + { + newchar->smartbomb->freeze = 1; + newchar->smartbomb->forcemap = MAP_TYPE_FREEZE; + newchar->smartbomb->attack_drop = 0; + } + else if(newchar->smartbomb->attack_type == ATK_STEAL) + { + newchar->smartbomb->steal = 1; + } + + if(newchar->type == TYPE_ITEM) + { + newchar->dofreeze = 0; // Items don't animate + newchar->smartbomb->freezetime = atoi(GET_ARGP(3)) * global_config.game_speed; + } + else + { + newchar->dofreeze = atoi(GET_ARGP(3)); // Are all animations frozen during special + newchar->smartbomb->freezetime = atoi(GET_ARGP(4)) * global_config.game_speed; + } +} + +/* +* Caskey, Damon V. +* 2019-11-22 +* +* Return type flag from string input. +*/ +e_entity_type get_type_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_entity_type flag; + } item_lookup_table[] = { + {"end_level", TYPE_ENDLEVEL}, + {"enemy", TYPE_ENEMY}, + {"item", TYPE_ITEM}, + {"no_copy", TYPE_NO_COPY}, + {"none", TYPE_NONE}, + {"npc", TYPE_NPC}, + {"obstacle", TYPE_OBSTACLE}, + {"playa", TYPE_PLAYER}, // :) + {"player", TYPE_PLAYER}, + {"projectile", TYPE_PROJECTILE}, + {"shot", TYPE_SHOT}, + {"steamer", TYPE_STEAMER}, + {"text_box", TYPE_TEXTBOX}, + {"trap", TYPE_TRAP}, + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown type (%s). \n", value); + return TYPE_UNKNOWN; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read entity type arguments directly from the source line + and combine their corresponding type flags. +*/ +e_entity_type get_type_from_command_line(const char* command_line) +{ + const char* value; + s_command_argument_reader reader; + e_entity_type result = TYPE_UNDECLARED; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= get_type_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Backport weapon loss conditions values +* to legacy weapon loss for backward +* compatability. +*/ +e_weapon_loss_condition_legacy weapon_loss_condition_interpret_to_legacy(e_weapon_loss_condition weapon_loss_condition_value) +{ + e_weapon_loss_condition_legacy result = WEAPLOSS_TYPE_ANY; + + if ((weapon_loss_condition_value & WEAPON_LOSS_CONDITION_DEFAULT) == WEAPON_LOSS_CONDITION_DEFAULT) + { + result |= WEAPLOSS_TYPE_ANY; + return result; + } + + if (weapon_loss_condition_value & WEAPON_LOSS_CONDITION_DEATH) + { + result |= WEAPLOSS_TYPE_DEATH; + return result; + } + + if (weapon_loss_condition_value & WEAPLOSS_TYPE_KNOCKDOWN) + { + result |= WEAPON_LOSS_CONDITION_FALL; + return result; + } + + if (weapon_loss_condition_value & WEAPLOSS_TYPE_CHANGE) + { + result |= WEAPON_LOSS_CONDITION_STAGE; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Interpret legacy weaploss commands into +* weapon loss bits per orginal documentation. +*/ +e_weapon_loss_condition weapon_loss_condition_interpret_from_legacy_weaploss(e_weapon_loss_condition weapon_loss_condition_value, e_weapon_loss_condition_legacy legacy_value) +{ + switch (legacy_value) + { + case WEAPLOSS_TYPE_ANY: + + weapon_loss_condition_value |= WEAPON_LOSS_CONDITION_DEFAULT; + break; + + case WEAPLOSS_TYPE_KNOCKDOWN: + + weapon_loss_condition_value |= WEAPON_LOSS_CONDITION_FALL; + break; + + case WEAPLOSS_TYPE_DEATH: + + weapon_loss_condition_value |= WEAPON_LOSS_CONDITION_PAIN; + break; + + case WEAPLOSS_TYPE_CHANGE: + + weapon_loss_condition_value |= WEAPON_LOSS_CONDITION_STAGE; + break; + + default: + + weapon_loss_condition_value |= WEAPON_LOSS_CONDITION_DEFAULT; + + break; + } + + return weapon_loss_condition_value; +} + +/* +* Caskey, Damon V. +* 2022-05-02 +* +* Accept string input and return +* matching constant. +*/ +e_weapon_loss_condition get_weapon_loss_from_argument(const char* value) +{ + e_weapon_loss_condition result; + + if (stricmp(value, "none") == 0) + { + result = WEAPON_LOSS_CONDITION_NONE; + } + if (stricmp(value, "damage") == 0) + { + result = WEAPON_LOSS_CONDITION_DAMAGE; + } + else if (stricmp(value, "death") == 0) + { + result = WEAPON_LOSS_CONDITION_DEATH; + } + else if (stricmp(value, "fall") == 0) + { + result = WEAPON_LOSS_CONDITION_FALL; + } + else if (stricmp(value, "grabbed") == 0) + { + result = WEAPON_LOSS_CONDITION_GRABBED; + } + else if (stricmp(value, "grabbing") == 0) + { + result = WEAPON_LOSS_CONDITION_GRABBING; + } + else if (stricmp(value, "land_damage") == 0) + { + result = WEAPON_LOSS_CONDITION_LAND_DAMAGE; + } + else if (stricmp(value, "pain") == 0) + { + result = WEAPON_LOSS_CONDITION_PAIN; + } + else if (stricmp(value, "stage") == 0) + { + result = WEAPON_LOSS_CONDITION_STAGE; + } + else if (stricmp(value, "default") == 0) + { + result = WEAPON_LOSS_CONDITION_DEFAULT; + } + else + { + result = WEAPON_LOSS_CONDITION_DEFAULT; + printf("\n\n Unknown weapon loss config value (%s), using 'default'. \n", value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-05-02 +* +* Populate weapon loss model property +* from text arguments. +*/ +void lcmHandleCommandWeaponLossCondition( + const char* command_line, + s_model* newchar +) +{ + const char* value; + s_command_argument_reader reader; + + newchar->weapon_properties.loss_condition = WEAPON_LOSS_CONDITION_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + newchar->weapon_properties.loss_condition |= get_weapon_loss_from_argument(value); + } + +} + +/* +* Caskey, Damon V. +* 2022-05-24 +* +* Read a text argument for model copy flag +* and output appropriate constant. If input +* is legacy integer, we just pass it on. +*/ +e_model_copy get_model_flag_from_argument( + const char* filename, + const char* command, + const char* value +) +{ + e_model_copy result = MODEL_COPY_FLAG_NONE; + + if (stricmp(value, "none") == 0) + { + result = MODEL_COPY_FLAG_NONE; + } + else if (stricmp(value, "no_basic") == 0) + { + result = MODEL_COPY_FLAG_NO_BASIC; + } + else if (stricmp(value, "no_weapon") == 0) + { + result = MODEL_COPY_FLAG_NO_WEAPON; + } + else if (stricmp(value, "no_script") == 0) + { + result = MODEL_COPY_FLAG_NO_SCRIPT; + } + else + { + /* + * For legacy compatability. Get integer + * value and then change it to current + * constants. + */ + + result = getValidInt(value, filename, command); + result = get_model_flag_from_legacy_int(result); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-01-30 +* +* Accept legacy integer model flag int +* and output current constant. +*/ +e_model_copy get_model_flag_from_legacy_int(int legacy_int) +{ + e_model_copy result = MODEL_COPY_FLAG_NONE; + + switch (legacy_int) + { + case 0: + default: + result = MODEL_COPY_FLAG_NONE; + case 1: + result = (MODEL_COPY_FLAG_NO_BASIC); + break; + case 3: + result = (MODEL_COPY_FLAG_NO_BASIC | MODEL_COPY_FLAG_NO_WEAPON); + break; + case 4: + result = (MODEL_COPY_FLAG_NO_BASIC | MODEL_COPY_FLAG_NO_WEAPON | MODEL_COPY_FLAG_NO_SCRIPT); + break; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-05-24 +* +* Populate model flag property +* from text arguments. +*/ +void lcmHandleCommandModelFlag( + char* filename, + char* command, + const char* command_line, + s_model* newchar +) +{ + const char* value; + s_command_argument_reader reader; + + newchar->model_flag = MODEL_COPY_FLAG_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + newchar->model_flag |= get_model_flag_from_argument(filename, command, value); + } + +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Backport air control values to legacy +* jumpmove for backward compatability. +*/ +e_air_control_legacy_x air_control_interpret_to_legacy_jumpmove_x(e_air_control air_control_value) +{ + e_air_control_legacy_x result = AIR_CONTROL_LEGACY_X_NONE; + + if (air_control_value & AIR_CONTROL_JUMP_TURN) + { + result |= AIR_CONTROL_LEGACY_X_FLIP; + } + + if (air_control_value & AIR_CONTROL_JUMP_X_ADJUST) + { + result |= AIR_CONTROL_LEGACY_X_ADJUST; + } + + if (air_control_value & AIR_CONTROL_JUMP_X_MOVE) + { + result |= AIR_CONTROL_LEGACY_X_MOVE; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Backport air control values to legacy +* jumpmove for backward compatability. +*/ +e_air_control_legacy_z air_control_interpret_to_legacy_jumpmove_z(e_air_control air_control_value) +{ + e_air_control_legacy_z result = AIR_CONTROL_LEGACY_Z_NONE; + + if (air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result |= AIR_CONTROL_LEGACY_Z_MOMENTUM; + } + + if (air_control_value & AIR_CONTROL_JUMP_Z_ADJUST) + { + result |= AIR_CONTROL_LEGACY_Z_ADJUST; + } + + if (air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result |= AIR_CONTROL_LEGACY_Z_MOMENTUM; + } + + if (air_control_value & AIR_CONTROL_JUMP_TURN && air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result = AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Backport air control values to legacy +* jumpmove for backward compatability. +*/ +e_air_control_legacy_x air_control_interpret_to_legacy_walkoffmove_x(e_air_control air_control_value) +{ + e_air_control_legacy_x result = AIR_CONTROL_LEGACY_X_NONE; + + if (air_control_value & AIR_CONTROL_WALKOFF_TURN) + { + result |= AIR_CONTROL_LEGACY_X_FLIP; + } + + if (air_control_value & AIR_CONTROL_WALKOFF_X_ADJUST) + { + result |= AIR_CONTROL_LEGACY_X_ADJUST; + } + + if (air_control_value & AIR_CONTROL_WALKOFF_X_MOVE) + { + result |= AIR_CONTROL_LEGACY_X_MOVE; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Backport air control values to legacy +* jumpmove for backward compatability. +*/ +e_air_control_legacy_z air_control_interpret_to_legacy_walkoffmove_z(e_air_control air_control_value) +{ + e_air_control_legacy_z result = AIR_CONTROL_LEGACY_Z_NONE; + + if (air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result |= AIR_CONTROL_LEGACY_Z_MOMENTUM; + } + + if (air_control_value & AIR_CONTROL_JUMP_Z_ADJUST) + { + result |= AIR_CONTROL_LEGACY_Z_ADJUST; + } + + if (air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result |= AIR_CONTROL_LEGACY_Z_MOMENTUM; + } + + if (air_control_value & AIR_CONTROL_JUMP_TURN && air_control_value & AIR_CONTROL_JUMP_Z_INITIAL) + { + result = AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Interpret legacy Jumpmove commands into +* air control bits per orginal documentaiton. +* +* low byte: 0 default 1 flip in air, 2 move in air, 3 flip and move +*/ +e_air_control air_control_interpret_from_legacy_jumpmove_x(e_air_control air_control_value, e_air_control_legacy_x legacy_value) +{ + switch (legacy_value) + { + case AIR_CONTROL_LEGACY_X_NONE: + + air_control_value &= ~(AIR_CONTROL_JUMP_TURN | AIR_CONTROL_JUMP_X_ADJUST | AIR_CONTROL_JUMP_X_MOVE); + break; + + case AIR_CONTROL_LEGACY_X_FLIP: + + air_control_value |= AIR_CONTROL_JUMP_TURN; + break; + + case AIR_CONTROL_LEGACY_X_ADJUST: + + air_control_value |= AIR_CONTROL_JUMP_X_ADJUST; + break; + + case AIR_CONTROL_LEGACY_X_ADJUST_AND_FLIP: + + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_X_ADJUST; + break; + + case AIR_CONTROL_LEGACY_X_MOVE: + case AIR_CONTROL_LEGACY_X_MOVE_ALT: + + air_control_value |= AIR_CONTROL_JUMP_X_MOVE; + break; + + case AIR_CONTROL_LEGACY_X_MOVE_AND_FLIP: + + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_X_MOVE; + break; + + default: + + /* + * Handle non-existent duplicate options + * listed in the legacy manual. + */ + + if (legacy_value > AIR_CONTROL_LEGACY_X_MOVE_ALT) + { + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_X_MOVE; + } + + break; + } + + return air_control_value; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* See interpret_legacy_jumpmove_x(); +*/ +e_air_control air_control_interpret_from_legacy_jumpmove_z(e_air_control air_control_value, e_air_control_legacy_z legacy_value) +{ + switch (legacy_value) + { + case AIR_CONTROL_LEGACY_Z_NONE: + + air_control_value &= ~(AIR_CONTROL_JUMP_TURN | AIR_CONTROL_JUMP_Z_ADJUST | AIR_CONTROL_JUMP_Z_INITIAL); + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM: + + air_control_value |= AIR_CONTROL_JUMP_Z_INITIAL; + break; + + case AIR_CONTROL_LEGACY_Z_ADJUST: + + air_control_value |= AIR_CONTROL_JUMP_Z_INITIAL; + air_control_value |= AIR_CONTROL_JUMP_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_ADJUST: + + air_control_value |= AIR_CONTROL_JUMP_Z_INITIAL; + air_control_value |= AIR_CONTROL_JUMP_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP: + + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_Z_MOVE; + + break; + + default: + + /* + * Handle non-existent duplicate options + * listed in the legacy manual. + */ + + if (legacy_value > AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP) + { + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_Z_MOVE; + } + + break; + + } + + return air_control_value; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Interpret legacy walkmove commands into +* air control bits per orginal documentaiton. +* +* low byte: 0 default 1 flip in air, 2 move in air, 3 flip and move +*/ +e_air_control air_control_interpret_from_legacy_walkoffmove_x(e_air_control air_control_value, e_air_control_legacy_x legacy_value) +{ + switch (legacy_value) + { + case AIR_CONTROL_LEGACY_X_NONE: + + air_control_value &= ~(AIR_CONTROL_WALKOFF_TURN | AIR_CONTROL_WALKOFF_X_ADJUST); + break; + + case AIR_CONTROL_LEGACY_X_FLIP: + + air_control_value |= AIR_CONTROL_WALKOFF_TURN; + break; + + case AIR_CONTROL_LEGACY_X_ADJUST: + + air_control_value |= AIR_CONTROL_WALKOFF_X_ADJUST; + break; + + case AIR_CONTROL_LEGACY_X_ADJUST_AND_FLIP: + case AIR_CONTROL_LEGACY_X_MOVE_AND_FLIP: + + air_control_value |= AIR_CONTROL_WALKOFF_TURN; + air_control_value |= AIR_CONTROL_WALKOFF_X_ADJUST; + break; + + case AIR_CONTROL_LEGACY_X_MOVE: + case AIR_CONTROL_LEGACY_X_MOVE_ALT: + + air_control_value |= AIR_CONTROL_WALKOFF_X_ADJUST; + break; + + default: + + /* + * Handle non-existent duplicate options + * listed in the legacy manual. + */ + + if (legacy_value > AIR_CONTROL_LEGACY_X_MOVE_ALT) + { + air_control_value |= AIR_CONTROL_WALKOFF_TURN; + air_control_value |= AIR_CONTROL_WALKOFF_X_ADJUST; + } + + break; + } + + return air_control_value; +} + +/* +* Caskey, Damon V. +* 2022-04-26 +* +* Interpret legacy walkmove commands into +* air control bits per orginal documentaiton. +* +* low byte: 0 default 1 flip in air, 2 move in air, 3 flip and move +*/ +e_air_control air_control_interpret_from_legacy_walkoffmove_z(e_air_control air_control_value, e_air_control_legacy_z legacy_value) +{ + switch (legacy_value) + { + case AIR_CONTROL_LEGACY_Z_NONE: + + air_control_value &= ~AIR_CONTROL_WALKOFF_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM: + + air_control_value |= AIR_CONTROL_WALKOFF_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_ADJUST: + + air_control_value |= AIR_CONTROL_WALKOFF_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_ADJUST: + + air_control_value |= AIR_CONTROL_WALKOFF_Z_ADJUST; + break; + + case AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP: + + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_WALKOFF_Z_ADJUST; + + break; + + default: + + /* + * Handle non-existent duplicate options + * listed in the legacy manual. + */ + + if (legacy_value > AIR_CONTROL_LEGACY_Z_MOMENTUM_AND_FLIP) + { + air_control_value |= AIR_CONTROL_JUMP_TURN; + air_control_value |= AIR_CONTROL_JUMP_Z_MOVE; + } + + break; + + } + + return air_control_value; +} + +/* +* Caskey, Damon V. +* 2022-04-28 +* +* Accept string input and return +* matching constant. +*/ +e_air_control find_air_control_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_air_control flag; + } item_lookup_table[] = { + {"none", AIR_CONTROL_NONE}, + {"jump_disable", AIR_CONTROL_JUMP_DISABLE}, + {"jump_turn", AIR_CONTROL_JUMP_TURN}, + {"jump_x_adjust", AIR_CONTROL_JUMP_X_ADJUST}, + {"jump_x_move", AIR_CONTROL_JUMP_X_MOVE}, + {"jump_x_stop", AIR_CONTROL_JUMP_X_STOP}, + {"jump_y_stop", AIR_CONTROL_JUMP_Y_STOP}, + {"jump_z_adjust", AIR_CONTROL_JUMP_Z_ADJUST}, + {"jump_z_initial", AIR_CONTROL_JUMP_Z_INITIAL}, + {"jump_z_move", AIR_CONTROL_JUMP_Z_MOVE}, + {"jump_z_stop", AIR_CONTROL_JUMP_Z_STOP}, + {"walkoff_turn", AIR_CONTROL_WALKOFF_TURN}, + {"walkoff_x_adjust", AIR_CONTROL_WALKOFF_X_ADJUST}, + {"walkoff_x_move", AIR_CONTROL_WALKOFF_X_MOVE}, + {"walkoff_x_stop", AIR_CONTROL_WALKOFF_X_STOP}, + {"walkoff_z_adjust", AIR_CONTROL_WALKOFF_Z_ADJUST}, + {"walkoff_z_move", AIR_CONTROL_WALKOFF_Z_MOVE}, + {"walkoff_z_stop", AIR_CONTROL_WALKOFF_Z_STOP}, + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown air control flag (%s). \n", value); + return AIR_CONTROL_NONE; +} + +/* +* Caskey, Damon V. +* 2022-04-28 +* +* Populate air control model property +* from text arguments. +*/ +void lcmHandleCommandAirControl( + const char* command_line, + s_model* newchar +) +{ + const char* value; + s_command_argument_reader reader; + + newchar->air_control = AIR_CONTROL_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + newchar->air_control |= find_air_control_from_string(value); + } + +} + +/* +* Caskey, Damon V. +* 2019-11-22 +* +* Accept string input and return +* matching constant. +*/ +e_move_config_flags find_move_config_flags_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_move_config_flags flag; + } item_lookup_table[] = { + {"none", MOVE_CONFIG_NONE}, + {"no_adjust_base", MOVE_CONFIG_NO_ADJUST_BASE}, + {"no_flip", MOVE_CONFIG_NO_FLIP}, + {"no_hit_head", MOVE_CONFIG_NO_HIT_HEAD}, + {"no_move", MOVE_CONFIG_NO_MOVE}, + {"projectile_base_die", MOVE_CONFIG_PROJECTILE_BASE_DIE}, + {"projectile_wall_bounce", MOVE_CONFIG_PROJECTILE_WALL_BOUNCE}, + {"subject_to_basemap", MOVE_CONFIG_SUBJECT_TO_BASEMAP}, + {"subject_to_gravity", MOVE_CONFIG_SUBJECT_TO_GRAVITY}, + {"subject_to_hole", MOVE_CONFIG_SUBJECT_TO_HOLE}, + {"subject_to_max_z", MOVE_CONFIG_SUBJECT_TO_MAX_Z}, + {"subject_to_min_z", MOVE_CONFIG_SUBJECT_TO_MIN_Z}, + {"subject_to_obstacle", MOVE_CONFIG_SUBJECT_TO_OBSTACLE}, + {"subject_to_platform", MOVE_CONFIG_SUBJECT_TO_PLATFORM}, + {"subject_to_screen", MOVE_CONFIG_SUBJECT_TO_SCREEN}, + {"subject_to_wall", MOVE_CONFIG_SUBJECT_TO_WALL} + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown move config flag (%s). \n", value); + return MOVE_CONFIG_NONE; +} + +/* +* Caskey, Damon V. +* 2021-08-24 +* +* Read a text argument for ko map type and +* output appropriate constant. If input is +* legacy integer, we just pass it on. +*/ +e_ko_colorset_config komap_type_get_value_from_argument(char* filename, char* command, char* value) +{ + e_ko_colorset_config result = KO_COLORSET_CONFIG_INSTANT; + + if (stricmp(value, "instant") == 0) + { + result = KO_COLORSET_CONFIG_INSTANT; + } + else if (stricmp(value, "finish") == 0) + { + result = KO_COLORSET_CONFIG_COMPLETE; + } + else + { + result = getValidInt(value, filename, command); + } + + return result; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read movement configuration arguments directly from the + source line and combine their corresponding behavior flags. +*/ +e_move_config_flags get_move_config_flags_from_command_line( + const char* command_line +) +{ + const char* value; + s_command_argument_reader reader; + e_move_config_flags result = MOVE_CONFIG_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= find_move_config_flags_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2022-04-28 +* +* Accept string input and return +* matching constant. +*/ +e_cheat_options find_cheat_options_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_cheat_options flag; + } item_lookup_table[] = { + {"none", CHEAT_OPTIONS_NONE}, + {"credits_active", CHEAT_OPTIONS_CREDITS_ACTIVE}, + {"credits_menu", CHEAT_OPTIONS_CREDITS_MENU}, + {"energy_active", CHEAT_OPTIONS_ENERGY_ACTIVE}, + {"energy_menu", CHEAT_OPTIONS_ENERGY_MENU}, + {"health_active", CHEAT_OPTIONS_HEALTH_ACTIVE}, + {"health_menu", CHEAT_OPTIONS_HEALTH_MENU}, + {"implacable_active", CHEAT_OPTIONS_IMPLACABLE_ACTIVE}, + {"implacable_menu", CHEAT_OPTIONS_IMPLACABLE_MENU}, + {"master_menu", CHEAT_OPTIONS_MASTER_MENU}, + {"lives_active", CHEAT_OPTIONS_LIVES_ACTIVE}, + {"lives_menu", CHEAT_OPTIONS_LIVES_MENU}, + {"multihit_active", CHEAT_OPTIONS_MULTIHIT_ACTIVE}, + {"multihit_menu", CHEAT_OPTIONS_MULTIHIT_MENU}, + {"touch_of_death_active", CHEAT_OPTIONS_TOD_ACTIVE}, + {"touch_of_death_menu", CHEAT_OPTIONS_TOD_MENU} + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown cheat option (%s). \n", value); + return CHEAT_OPTIONS_NONE; + +} + +/* +* Caskey, Damon V. +* 2022-04-28 +* +* Populate global config cheats +* property from text arguments. +*/ +void lcmHandleCommandGlobalConfigCheats(const char* command_line) +{ + const char* value; + s_command_argument_reader reader; + + global_config.cheats = CHEAT_OPTIONS_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + global_config.cheats |= find_cheat_options_from_string(value); + } + +} + +/* +* Caskey, Damon V. +* 2022-06-08 +* +* Accept string input and return +* matching constant. +*/ +e_aimove get_aimove_constant_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_aimove flag; + } item_lookup_table[] = { + {"none", AIMOVE1_NONE}, + {"default", AIMOVE_SPECIAL_DEFAULT}, + {"chase", AIMOVE1_CHASE}, + {"chasez", AIMOVE1_CHASEZ}, + {"chasex", AIMOVE1_CHASEX}, + {"avoid", AIMOVE1_AVOID}, + {"avoidz", AIMOVE1_AVOIDZ}, + {"avoidx", AIMOVE1_AVOIDX}, + {"wander", AIMOVE1_WANDER}, + {"biker", AIMOVE1_BIKER}, + {"arrow", AIMOVE1_ARROW}, + {"star", AIMOVE1_STAR}, + {"bomb", AIMOVE1_BOMB}, + {"nomove", AIMOVE1_NOMOVE}, + {"ignoreholes", AIMOVE2_IGNOREHOLES}, + {"notargetidle", AIMOVE2_NOTARGETIDLE} + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown AIMove flag (%s). \n", value); + return AIMOVE1_NONE; + +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read AI movement arguments directly from the source line + and combine them with the supplied default behavior flags. +*/ +e_aimove get_aimove_from_command_line( + const char* command_line, + e_aimove default_value +) +{ + const char* value; + s_command_argument_reader reader; + e_aimove result = default_value; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= get_aimove_constant_from_string(value); + } + + return result; +} + +void lcmHandleCommandAiattack(ArgList *arglist, s_model *newchar, int *aiattackset, char *filename) +{ + char *value = GET_ARGP(1); + if(!*aiattackset) + { + newchar->aiattack = 0; + *aiattackset = 1; + } + + //main A.I. move switches + if(value && value[0]) + { + if(stricmp(value, "normal") == 0) + { + newchar->aiattack |= AIATTACK1_NORMAL; + } + else if(stricmp(value, "always") == 0) + { + newchar->aiattack |= AIATTACK1_ALWAYS; + } + else if(stricmp(value, "noattack") == 0) + { + newchar->aiattack |= AIATTACK1_NOATTACK; + } + else + { + printf("Model '%s' has invalid A.I. attack switch: '%s'\n", filename, value); + } + } + /* + value = GET_ARGP(2); + //sub A.I. move switches + if(value && value[0]) + { + + }*/ +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read weapon model names directly from the source line, + allocate an exact-sized list, and safely replace any list + owned by the model without modifying inherited storage. +*/ +void lcmHandleCommandWeapons( + const char* command_line, + s_model* newchar +) +{ + const char* value; + s_command_argument_reader reader; + s_command_token token; + s_command_token_reader count_reader = { + .cursor = command_line + }; + int* weapon_list; + size_t weapon_count = 0; + size_t weapon_index = 0; + + /* Skip the command name before counting its values. */ + command_token_reader_next(&count_reader, &token); + + while(command_token_reader_next(&count_reader, &token)) { + if(weapon_count == INT_MAX) { + borShutdown( + 1, + "Weapon list exceeds the supported integer count range.\n" + ); + } + + weapon_count++; + } + + if(!weapon_count) { + return; + } + + if(weapon_count > SIZE_MAX / sizeof(*weapon_list)) { + borShutdown(1, E_OUT_OF_MEMORY); + } + + weapon_list = malloc(sizeof(*weapon_list) * weapon_count); + + if(!weapon_list) { + borShutdown(1, E_OUT_OF_MEMORY); + } + + /* + * Weapon list arguments left to right, until we + * reach this model's number of weapons. If none, + * populate with "none" index. Otherwise we find + * a model index to populate with. + */ + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + weapon_list[weapon_index] = stricmp(value, "none") != 0 + ? get_cached_model_index(value) + : MODEL_INDEX_NONE; + + weapon_index++; + } + + if(hasFreetype(newchar, MF_WEAPONS) + && newchar->weapon_properties.weapon_list) { + free(newchar->weapon_properties.weapon_list); + } + + newchar->weapon_properties.weapon_list = weapon_list; + newchar->weapon_properties.weapon_count = (int)weapon_count; + newchar->weapon_properties.weapon_state |= WEAPON_STATE_HAS_LIST; + newchar->freetypes |= MF_WEAPONS; +} + +//fetch string between next @script and @end_script +//return end position of @end_script, count from current position +static size_t fetchInlineScript(char *buf, char **scriptbuf, ptrdiff_t *ppos, size_t *plen) +{ + ptrdiff_t pos = *ppos; + size_t len; + while(!starts_with(buf + pos, "@script")) + { + pos++; + } + pos += strclen("@script"); + len = 0; + while(!starts_with(buf + pos, "@end_script")) + { + len++; + pos++; + } + *scriptbuf = malloc(sizeof(**scriptbuf) * len + 1); + strncpy(*scriptbuf, buf + pos - len, len); + (*scriptbuf)[len] = 0; + pos += strclen("@end_script"); + + *ppos = pos; + *plen = len; + return pos; +} + +size_t lcmHandleCommandScripts(ArgList *arglist, char *buf, Script *script, char *scriptname, char *filename, int compile, int first) +{ + ptrdiff_t pos = 0; + size_t len = 0; + int result = 0; + char *scriptbuf = NULL; + Script_Init(script, scriptname, filename, first); + if(stricmp(GET_ARGP(1), "@script") == 0) + { + fetchInlineScript(buf, &scriptbuf, &pos, &len); + if(scriptbuf) + { + result = Script_AppendText(script, scriptbuf, filename); + free(scriptbuf); + } + } + else + { + result = load_script(script, GET_ARGP(1)); + } + if(result) + { + if(compile) + { + Script_Compile(script); + } + } + else + { + borShutdown(1, "Unable to load %s '%s' in file '%s'.\n", scriptname, GET_ARGP(1), filename); + } + return pos; +} + +ptrdiff_t lcmScriptAvoidComment(char *buf, size_t buf_len, ptrdiff_t pos) +{ + if ( buf && buf[0] ) + { + unsigned char c = '\0'; + ptrdiff_t pre_pos = pos; + + c = buf[pos]; + if (c == '/') + { + ++pos; + if ( pos < buf_len-1 ) + { + c = buf[pos]; + if (c == '/') + { + while( pos < buf_len && c != 0x0D && c != 0x0A ) + { + ++pos; + c = buf[pos]; + } + if (pos < buf_len) ++pos; + pos = lcmScriptAvoidComment(buf,buf_len,pos); + return pos; + } + else if (c == '*') + { + ++pos; + if ( pos >= buf_len ) return pos; + c = buf[pos]; + while( pos+1 < buf_len ) + { + if ( c == '*' ) + { + unsigned char c2 = buf[pos+1]; + + if ( c2 == '/' ) + { + pos += 2; + pos = lcmScriptAvoidComment(buf,buf_len,pos); + return pos; + } + } + ++pos; + c = buf[pos]; + } + } + else + { + return pre_pos; + } + } + } else return pre_pos; + } + + return pos; +} + +ptrdiff_t lcmScriptGetMainPos(char *buf, size_t *buf_len) +{ + if ( buf && buf[0] ) + { + size_t len = *buf_len; + ptrdiff_t pos = 0; + enum {START,PRE0,PRE1,PRE2,PRE3,M0,M1,M2,M3,P0,P1,END} current_state = START; + unsigned char c = '\0'; + int index_res = -1; + + pos = lcmScriptAvoidComment(buf,len,pos); + c = (unsigned char)tolower((int)buf[pos]); + while ( pos < len && current_state != END ) + { + switch(current_state) + { + case START: + { + if ( c == 'v' ) + { + index_res = pos; // store void main() start pos + current_state = PRE0; + } + else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = START; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case PRE0: + { + if ( c == 'o' ) current_state = PRE1; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case PRE1: + { + if ( c == 'i' ) current_state = PRE2; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case PRE2: + { + if ( c == 'd' ) current_state = PRE3; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case PRE3: + { + if ( c == 'm' ) current_state = M0; + else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = PRE3; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case M0: + { + if ( c == 'a' ) current_state = M1; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case M1: + { + if ( c == 'i' ) current_state = M2; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case M2: + { + if ( c == 'n' ) current_state = M3; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case M3: + { + if ( c == '(' ) current_state = P0; + else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = M3; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case P0: + { + if ( c == ')' ) current_state = P1; + else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P0; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case P1: + { + if ( c == '{' ) + { + current_state = END; + if (++pos < len) + { + *buf_len = pos-index_res; + return index_res; + } + else return -1; + } + else if ( c == ' ' || c == '\n' || c == '\r' || c == 0x0D || c == 0x0A || c == '\t' ) current_state = P1; + else if ( c == '\0' ) return -1; + else current_state = START; + break; + } + case END: + default: + break; + } + ++pos; // at the end position after the '{' + if (current_state == END) + { + if (pos < len) + { + *buf_len = pos-index_res; + return index_res; + } + else return -1; + } + pos = lcmScriptAvoidComment(buf,len,pos); + c = (unsigned char)tolower((int)buf[pos]); + } // end while loop + + if (current_state == END) + { + if (pos < len) + { + *buf_len = pos-index_res; + return index_res; + } + else return -1; + } else return -1; + } + + return -1; +} + +// add main (or add vars in main) - used for animationscript file +size_t lcmScriptAddMain(char **buf) +{ + size_t len = 0, len2 = 0, buf_len = 0; + ptrdiff_t pos = 0; + char* newbuf = NULL; + + if ( (*buf) && (*buf)[0] ) + { + buf_len = len = strlen(*buf); + + pos = lcmScriptGetMainPos(*buf,&buf_len); + if ( pos == -1 ) // main() not found! + { + char mtxt[] = "\n\nvoid main()\n{\n int frame = getlocalvar(\"frame\");\n int animhandle = getlocalvar(\"animhandle\");\n\n}\n\n"; + + pos += buf_len; + pos = len; // pos before '\0' (at last char) + len2 = strlen(mtxt); + newbuf = calloc(sizeof(**buf)*len + sizeof(mtxt)*len2 + 1, sizeof(char)); + strncpy(newbuf, *buf, pos); + strcpy(newbuf+pos, mtxt); + newbuf[len+len2] = '\0'; + + free( (*buf) ); + (*buf) = newbuf; + + //printf("written main in buffer\n%s\n",newbuf); + } + else + { + int pos2 = 0; + char mtxt[] = "\n\nvoid main()\n{\n int frame = getlocalvar(\"frame\");\n int animhandle = getlocalvar(\"animhandle\");\n\n"; + + pos2 = pos + buf_len; + len2 = strlen(mtxt); + + newbuf = calloc(sizeof(**buf)*pos + sizeof(mtxt)*len2 + sizeof(**buf)*(len-pos2) + 1, sizeof(char)); + strncpy(newbuf, *buf, pos); + strcpy(newbuf+pos, mtxt); + strncpy(newbuf+pos+len2, *buf+pos2, len-pos2); + newbuf[pos+len2+len-pos2] = '\0'; + + free( (*buf) ); + (*buf) = newbuf; + + //printf("search for main in buffer\n%s pos:%d\n",*buf,pos); + } + + //printf("search for main in buffer\n%s pos:%d\n",*buf,pos); + } + + return len; +} + +// used to join animationscript @script/@cmd width animationscript file main() +size_t lcmScriptJoinMain(char **buf, char *first_buf) +{ + size_t len = 0, len2 = 0, buf_len = 0; + ptrdiff_t pos = 0; + int pcount = 0; + char* newbuf = NULL; + + if ( (*buf) && first_buf && (*buf)[0] && first_buf[0] ) + { + buf_len = len = strlen(*buf); + len2 = strlen(first_buf); + + pos = lcmScriptGetMainPos(*buf,&buf_len); + pos += buf_len; + + pcount = 1; + while(pcount > 0) + { + if( (*buf)[pos] == '{' ) ++pcount; + else if( (*buf)[pos] == '}' ) --pcount; + pos++; + } + --pos; // back to last '}' of main() + + newbuf = calloc(sizeof(**buf)*len + sizeof(*first_buf)*len2 + 1,sizeof(char)); + strncpy(newbuf, *buf, pos); + strcpy(newbuf+pos, first_buf); + strncpy(newbuf+pos+len2, *buf+pos, len-pos); + newbuf[len+len2] = '\0'; + + free( (*buf) ); + (*buf) = newbuf; + + //printf("test joined buffer\n%s\n",*buf); + } + + return len; +} + +size_t lcmScriptCopyBuffer(ArgList *arglist, char *buf, char **script_buffer) +{ + ptrdiff_t pos = 0; + size_t len = 0; + + if(stricmp(GET_ARGP(1), "@script") == 0) + { + fetchInlineScript(buf, script_buffer, &pos, &len); + } + else + { + buffer_pakfile(GET_ARGP(1), script_buffer, &len); + } + + //printf("test buffer\n%s\n",*script_buffer); + return pos; +} + +size_t lcmScriptDeleteMain(char **buf) +{ + size_t len = 0, i = 0; + ptrdiff_t pos = 0; + char *newbuf = NULL; + + if ((*buf) && (*buf)[0]) + { + len = strlen(*buf); + + while(!starts_with((*buf) + pos, "main()")) pos++; + pos += strclen("main()"); + while(!starts_with(*buf + pos, "{")) pos++; + pos += strclen("{"); + while(!starts_with(*buf + pos, "int frame = getlocalvar(\"frame\");")) pos++; + pos += strclen("int frame = getlocalvar(\"frame\");"); + while(!starts_with(*buf + pos, "int animhandle = getlocalvar(\"animhandle\");\n")) pos++; + pos += strclen("int animhandle = getlocalvar(\"animhandle\");\n"); + + for(i = len-1; ; i--) + { + if ( (*buf)[i] == '}' ) + { + len = i; + break; + } else continue; + //if (i <= 0) break; + } + + len = len-pos; + newbuf = malloc(sizeof(newbuf) * (len) + 1); + strncpy(newbuf, *buf+pos, len); + newbuf[len] = '\0'; + + free( (*buf) ); + (*buf) = newbuf; + + //printf("test delete main\n%s\n",newbuf); + } + + return len; +} + +//alloc a new model, and everything thats required, +//set all values to defaults +s_model *init_model(const int cacheindex, const int unload) +{ + //to free: newchar, newchar->offense_factors, newchar->special, newchar->animation - OK + int i; + + s_model* const newchar = calloc(1, sizeof(*newchar)); + if(!newchar) + { + borShutdown(1, (char *)E_OUT_OF_MEMORY); + } + newchar->object_type = OBJECT_TYPE_MODEL; + newchar->test_pointer = model_cache[cacheindex].name; + strcpy(newchar->test_fixed, model_cache[cacheindex].name); + + newchar->name = model_cache[cacheindex].name; // well give it a name for sort method + newchar->index = cacheindex; + newchar->isSubclassed = 0; + newchar->freetypes = MF_ALL; + + newchar->priority = 1; + + newchar->defense = defense_allocate_object(); + newchar->offense = offense_allocate_object(); + + newchar->special = calloc(1, sizeof(s_com)); + + alloc_all_scripts(&newchar->scripts); + + + + newchar->death_config_flags = DEATH_CONFIG_MACRO_DEFAULT; + + newchar->edelay = (s_edelay){ + .cap = { + .max = MAX_INT, + .min = 0 + }, + .range = { + .max = MAX_INT, + .min = 0 + }, + .factor = 1.0f + }; + + newchar->unload = unload; + newchar->jumpspecial = 0; // Kratus (10-2021) Added new property to kill or not the default jumpspecial movement + newchar->jumpspeed = default_model_jumpspeed; + newchar->jumpheight = default_model_jumpheight; // 28-12-2004 Set default jump height to 4, if not specified + newchar->runjumpheight = default_model_jumpheight; // Default jump height if a player is running + newchar->runjumpdist = 1; // Default jump distane if a player is running + newchar->grabdistance = default_model_grabdistance; // 30-12-2004 Default grabdistance is same as originally set + newchar->grabflip = 3; + newchar->throwdamage = 21; //21 // default throw damage + + newchar->icon = (s_icon){ + .def = ICON_NONE, + .die = ICON_NONE, + .get = ICON_NONE, + .mphigh = ICON_NONE, + .mplow = ICON_NONE, + .mpmax = ICON_NONE, + .mpmed = ICON_NONE, + .mpnone = ICON_NONE, + .pain = ICON_NONE, + .weapon = ICON_NONE + }; + + newchar->diesound = SAMPLE_ID_NONE; + newchar->nolife = 0; // default show life = 1 (yes) + newchar->shadow_config_flags = SHADOW_CONFIG_DEFAULT; + newchar->remove_config = REMOVE_CONFIG_HIT; // Flag set to projectiles are removed upon hitting an opponent + newchar->throwdist = default_model_jumpheight * 0.625f; + newchar->aimove = AIMOVE1_NONE; + newchar->aiattack = -1; + newchar->throwframewait = FRAME_NONE; // makes sure throw animations run normally unless throwfram is specified, added by kbandressen 10/20/06 + newchar->path = model_cache[cacheindex].path; // Record path, so script can get it without looping the whole model collection. + newchar->edgerange.x = 0; + newchar->edgerange.z = 0; + + newchar->colorsets = (s_colorset){ + .burn = COLORSET_INDEX_NONE, + .frozen = COLORSET_INDEX_NONE, + .hide_end = COLORSET_INDEX_NONE, + .hide_start = COLORSET_INDEX_NONE, + .ko = COLORSET_INDEX_NONE, + .kotype = KO_COLORSET_CONFIG_INSTANT, + .shock = COLORSET_INDEX_NONE + }; + + // Default Attack1 in chain must be referenced if not used. + for(i = 0; i < MAX_ATCHAIN; i++) + { + newchar->atchain[i] = 1; + } + newchar->chainlength = 1; + + if(magic_type == 1) + { + newchar->mprate = 1; + } + else + { + newchar->mprate = 2; + } + newchar->chargerate = newchar->guardrate = 2; + newchar->risetime.rise = -1; + newchar->sleepwait = 1000; + + newchar->guardpoints = 0; + newchar->jugglepoints = 0; + + newchar->mpswitch = -1; // switch between reduce mp or gain mp for mpstabletype 4 + + newchar->weapon_properties = (s_weapon){ + .loss_condition = WEAPON_LOSS_CONDITION_DEFAULT, + .loss_count = 3, + .loss_index = MODEL_INDEX_NONE + }; + + newchar->lifespan = LIFESPAN_DEFAULT; + newchar->summonkill = 1; + + /* + * Faction data. Faction type properties + * get defaults set downstream depending + * on the model's own type. + */ + + newchar->faction = (s_faction){ + .damage_direct = FACTION_GROUP_DEFAULT, + .damage_indirect = FACTION_GROUP_DEFAULT, + .hostile = FACTION_GROUP_DEFAULT, + .member = FACTION_GROUP_DEFAULT, + .type_damage_direct = TYPE_UNDECLARED, + .type_damage_indirect = TYPE_UNDECLARED, + .type_hostile = TYPE_UNDECLARED, + .object_type = OBJECT_TYPE_FACTION + }; + + newchar->move_config_flags = MOVE_CONFIG_NONE; + newchar->pshotno = MODEL_INDEX_NONE; + newchar->project = MODEL_INDEX_NONE; + + newchar->dust = (s_dust){ + .fall_land = MODEL_INDEX_NONE, + .jump_land = MODEL_INDEX_NONE, + .jump_start = MODEL_INDEX_NONE + }; + + newchar->bomb = MODEL_INDEX_NONE; + newchar->star = MODEL_INDEX_NONE; + newchar->knife = MODEL_INDEX_NONE; + newchar->stealth.hide = 0; + newchar->stealth.detect = 0; + newchar->attackthrottle = 0.0f; + newchar->attackthrottletime = noatk_duration * global_config.game_speed; + + /* + * max_animations originates in data/models.txt. Check + * the per-model table multiplication before allocating. + */ + if(max_animations <= 0 + || (size_t)max_animations + > SIZE_MAX / sizeof(*newchar->animation)) { + borShutdown( + 1, + "Invalid per-model animation table size (%d). " + "Check animation limits in data/models.txt.\n", + max_animations + ); + } + + newchar->animation = calloc( + (size_t)max_animations, + sizeof(*newchar->animation) + ); + if(!newchar->animation) + { + borShutdown(1, (char *)E_OUT_OF_MEMORY); + } + + // default string value, only by reference + newchar->rider = get_cached_model_index("K'"); + newchar->flash.model_hit = newchar->flash.model_block = get_cached_model_index("flash"); + + //Default sight ranges. + newchar->sight = (s_sight){ + .max = {.x = MAX_INT, .y = MAX_INT, .z = MAX_INT }, + .min = {.x = MIN_INT, .y = MIN_INT, .z = MIN_INT } + }; + + return newchar; +} + +void update_model_loadflag(s_model *model, char unload) +{ + model->unload = unload; +} + +s_model *load_cached_model(char *name, char *owner, char unload) +{ + #define LOG_CMD_TITLE "%-20s" + + s_model* newchar = NULL; + s_model* tempmodel = NULL; + + s_anim *newanim = NULL; + + char* filename = NULL; + char* buf = NULL; + char* animscriptbuf = NULL; + char* scriptbuf = NULL; + char* command = NULL; + char* value = NULL; + char* value2 = NULL; + + char fnbuf[MAX_BUFFER_LEN] = { "" }; + char namebuf[MAX_BUFFER_LEN] = { "" }; + char argbuf[MAX_ARG_LEN + 1] = { "" }; + + ArgList arglist; + + int ani_id = ANI_NONE; + int script_id = -1; + int frm_id = -1; + bool at_cmd_mergeable = false; + int i = 0; + int tempInt = 0; + int framecount = 0; + int frameset = 0; + int peek = 0; + int cacheindex = 0; + int curframe = 0; + int errorVal = 0; + int shadow_set = 0; + int idle = 0; + int frameshadow = FRAME_SHADOW_NONE; // FRAME_SHADOW_NONE will use default shadow for this entity, otherwise will use this value. + int temp_frame_sound_index = 0; + int aiattackset = 0; + int maskindex = -1; + int nopalette = 0; + + size_t size = 0; + size_t line = 0; + size_t len = 0; + size_t sbsize = 0; + size_t scriptlen = 0; + + uint64_t delay = 0; + + e_delay_unit delay_mode = DELAY_UNIT_GLOBAL; + + ptrdiff_t pos = 0; + ptrdiff_t index = 0; + + s_addframe_data add_frame_data; + + s_axis_plane_vertical_int offset = { .x = 0, + .y = 0 }; + int shadow_xz[2] = {0, 0}; + int shadow_coords[2] = {0, 0}; + + float platform[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + float platform_con[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + s_move move = { { .x = 0, + .y = 0, + .z = 0}, + .base = -1 //-1 = Disabled, 0+ base set + }; + + s_drawmethod drawmethod; + s_drawmethod dm; + + s_frame_sound_collection* temp_frame_sound = NULL; + + /* + * Caskey, Damon V. + * 2026-06-28 (updated from 2021 node header) + * + * Temporary collision collections. As we parse the + * model file, we will build a temporary collection + * of collision data, then clone it into the model's + * target animation frame, and the discard the temporary + * collection. + * + * Index tells us which collision index to target. Any + * time we are updating a collision box, we pass the index + * to the update functions. The parse for index must + * therefore appear before any other collision parsing + * in order, so it modifies the index before we call the + * collision update functions. + */ + int temp_collision_index = 0; + s_collision_collection* temp_collision_attack = NULL; + s_collision_collection* temp_collision_body = NULL; + s_collision_collection* temp_collision_space = NULL; + + /* + * Temp pointer to make adding several collision + * coordinates in one command a little cleaner. + */ + s_hitbox* temp_collision_coordinates = NULL; + + int temp_child_spawn_index = 0; + s_child_spawn* temp_child_spawn_head = NULL; // Spawning sub entities. + + const char *shutdownmessage = NULL; + + unsigned* mapflag = NULL; // in 24bit mode, we need to know whether a colourmap is a common map or a palette + + char alert_buffer[256] = { 0 }; // So we can concatenate specifics *like range max/min* into alert messages. + + const char pre_text[] = // this is the skeleton of frame function + { + "void main()\n" + "{\n" + " int frame = getlocalvar(\"frame\");\n" + " int animhandle = getlocalvar(\"animhandle\");\n" + "\n}\n" + }; + + const char sur_text[] = // end of function text + { + "\n}\n" + }; + + const char ifid_text[] = // if expression to check animation id + { + " if(animhandle==%" PRId64 ")\n" + " {\n" + " return;\n" + " }\n" + }; + + const char endifid_text[] = // end of if + { + " return;\n" + " }\n" + }; + + const char if_text[] = // this is the if expression of frame function + { + " if(frame==%d)\n" + " {\n" + }; + + const char endif_return_text[] = //return to reduce unecessary checks + { + " return;\n" + }; + + const char endif_text[] = // end of if + { + " }\n" + } ; + + const char comma_text[] = // arguments separator + { + ", " + }; + + const char call_indent_text[] = //begin of function call + { + " " + }; + + const char call_open_text[] = + { + "(" + }; + + const char endcall_text[] = //end of function call + { + ");\n" + }; + + modelCommands cmd; + s_scripts* tempscripts; + +#ifdef DEBUG + printf("load_cached_model: %s, unload: %d\n", name, unload); +#endif + + // Start up the standard log entry. + printf("Loading '%s'", name); + + // Model already loaded but we might want to unload after level is completed. + if((tempmodel = findmodel(name)) != NULL) + { + update_model_loadflag(tempmodel, unload); + cache_model_sprites(tempmodel, 1); + return tempmodel; + } + + cacheindex = get_cached_model_index(name); + if(cacheindex < 0) + { + borShutdown(1, "Fatal: No cache entry for '%s' within '%s'\n\n", name, owner); + } + + model_cache[cacheindex].lifecycle = MODEL_LIFECYCLE_LOADING; + + // Get the text file name of model. + filename = model_cache[cacheindex].path; + printf(" from %s \n", filename); + + if(buffer_pakfile(filename, &buf, &size) != 1) + { + borShutdown(1, "Unable to open file '%s'\n\n", filename); + } + + sbsize = size + 1; + scriptbuf = (char *)malloc(sbsize); + + if(scriptbuf == NULL) + { + borShutdown(1, "Unable to create script buffer for file '%s' (%i bytes)", filename, size * 2); + } + scriptbuf[0] = 0; + + //_peek_model_name(cacheindex); + newchar = init_model(cacheindex, unload); + //newchar->name = name; + + //attention, we increase models_loaded here, this can be dangerous if we access that value later on, + //since recursive calls will change it! + models_loaded++; + addModel(newchar); + + drawmethod = plainmethod; // better than memset it to 0 + + newchar->hitwalltype = -1; // init to -1 + + //char* test = "load knife 0"; + //ParseArgs(&arglist,test,argbuf); + + // Now interpret the contents of buf line by line + while(pos < size) + { + //command = GET_ARG(0); + line++; + if(ParseArgs(&arglist, buf + pos, argbuf)) + { + command = GET_ARG(0); + cmd = getModelCommand(modelcmdlist, command); + + //if (cmd != CMD_MODEL_FRAME) framenum = 0; + + switch(cmd) + { + case CMD_MODEL_SUBCLASS: + //inherit everything from an existing, cached model + tempmodel = findmodel(GET_ARG(1)); + if (!tempmodel) + { + shutdownmessage = "tried to subclass a non-existing/not previously loaded model!"; + goto lCleanup; + } + tempscripts = newchar->scripts; + *newchar = *tempmodel; + newchar->scripts = tempscripts; + copy_all_scripts(tempmodel->scripts, newchar->scripts, 1); + newchar->isSubclassed = 1; + newchar->freetypes = MF_SCRIPTS; + break; + case CMD_MODEL_NAME: + lcmHandleCommandName(&arglist, newchar, cacheindex); + break; + case CMD_MODEL_TYPE: + lcmHandleCommandType(&arglist, newchar, filename); + break; + case CMD_MODEL_SUBTYPE: + lcmHandleCommandSubtype(&arglist, newchar, filename); + break; + case CMD_MODEL_HEALTH: + value = GET_ARG(1); + newchar->health = atoi(value); + break; + case CMD_MODEL_PRIORITY: + value = GET_ARG(1); + newchar->priority = atoi(value); + break; + case CMD_MODEL_SCROLL: + value = GET_ARG(1); + newchar->scroll = atof(value); + break; + case CMD_MODEL_MP: //Left for backward compatability. See mpset. // mp values to put max mp for player by tails + value = GET_ARG(1); + newchar->mp = atoi(value); + break; + case CMD_MODEL_NOLIFE: // Feb 25, 2005 - Flag to display enemy life or not + newchar->nolife = GET_INT_ARG(1); + break; + case CMD_MODEL_MAKEINV: // Mar 12, 2005 - If a value is supplied, corresponds to amount of time the player spawns invincible + newchar->makeinv = GET_FLOAT_ARG(1) * global_config.game_speed; + if(GET_INT_ARG(2)) + { + newchar->makeinv = -newchar->makeinv; + } + break; + case CMD_MODEL_RISEINV: + newchar->riseinv = GET_FLOAT_ARG(1) * global_config.game_speed; + if(GET_INT_ARG(2)) + { + newchar->riseinv = -newchar->riseinv; + } + break; + case CMD_MODEL_LOAD: + value = GET_ARG(1); + tempmodel = findmodel(value); + if(!tempmodel) + { + load_cached_model(value, name, GET_INT_ARG(2)); + } + else + { + update_model_loadflag(tempmodel, GET_INT_ARG(2)); + } + break; + case CMD_MODEL_SCORE: + if(!command_argument_get_int64(GET_ARG(1), &newchar->score)) + { + borShutdown( + 1, + "Invalid signed 64-bit score '%s' in %s, line %zu.\n", + GET_ARG(1), + filename, + line + ); + } + newchar->multiple = GET_INT_ARG(2); // New var multiple for force/scoring + break; + case CMD_MODEL_SMARTBOMB: + lcmHandleCommandSmartbomb(&arglist, newchar, filename); + break; + case CMD_MODEL_BOUNCE: + newchar->bounce = GET_INT_ARG(1); + break; + case CMD_MODEL_NOQUAKE: // Mar 12, 2005 - Flag to determine if entity shakes screen + newchar->quake_config |= GET_INT_ARG(1) ? QUAKE_CONFIG_DISABLE_SCREEN : 0; + newchar->quake_config |= GET_INT_ARG(2) ? QUAKE_CONFIG_DISABLE_SELF : 0; + break; + case CMD_MODEL_BLOCK_CONFIG: + + newchar->block_config_flags = block_get_config_flags_from_command_line(buf + pos); + break; + + case CMD_MODEL_BLOCKBACK: + + /* Legacy value to bitmask. */ + + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->block_config_flags |= BLOCK_CONFIG_BACK; + } + else + { + newchar->block_config_flags &= ~BLOCK_CONFIG_BACK; + } + + break; + + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_CONFIG: + + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_config = direction_get_adjustment_from_argument(filename, command, GET_ARG(1)); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_BASE_MAX: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.base.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_BASE_MIN: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.base.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_X_MAX: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.x.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_X_MIN: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.x.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_Y_MAX: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.y.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_Y_MIN: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.y.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_Z_MAX: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.z.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_DIRECTION_ADJUST_RANGE_Z_MIN: + child_follow_getsert_property(&newchar->child_follow)->direction_adjust_range.z.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_CHILD_FOLLOW_OFFSET_X: + + child_follow_getsert_property(&newchar->child_follow)->follow_offset.x = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_OFFSET_Y: + child_follow_getsert_property(&newchar->child_follow)->follow_offset.y = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_OFFSET_Z: + child_follow_getsert_property(&newchar->child_follow)->follow_offset.z = GET_INT_ARG(1); + break; + + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_BASE_MAX: + child_follow_getsert_property(&newchar->child_follow)->recall_range.base.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_BASE_MIN: + child_follow_getsert_property(&newchar->child_follow)->recall_range.base.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_X_MAX: + child_follow_getsert_property(&newchar->child_follow)->recall_range.x.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_X_MIN: + child_follow_getsert_property(&newchar->child_follow)->recall_range.x.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_Y_MAX: + child_follow_getsert_property(&newchar->child_follow)->recall_range.y.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_Y_MIN: + child_follow_getsert_property(&newchar->child_follow)->recall_range.y.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_Z_MAX: + child_follow_getsert_property(&newchar->child_follow)->recall_range.z.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RECALL_Z_MIN: + child_follow_getsert_property(&newchar->child_follow)->recall_range.z.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_BASE_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.base.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_BASE_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.base.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_X_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.x.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_X_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.x.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_Y_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.y.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_Y_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.y.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_Z_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.z.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_RUN_Z_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_run_range.z.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_CHILD_FOLLOW_RANGE_BASE_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_range.base.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_BASE_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_range.base.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_X_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_range.x.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_X_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_range.x.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_Y_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_range.y.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_Y_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_range.y.min = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_Z_MAX: + child_follow_getsert_property(&newchar->child_follow)->follow_range.z.max = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RANGE_Z_MIN: + child_follow_getsert_property(&newchar->child_follow)->follow_range.z.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_CHILD_FOLLOW_RECALL_OFFSET_X: + + child_follow_getsert_property(&newchar->child_follow)->recall_offset.x = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RECALL_OFFSET_Y: + child_follow_getsert_property(&newchar->child_follow)->recall_offset.y = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_FOLLOW_RECALL_OFFSET_Z: + child_follow_getsert_property(&newchar->child_follow)->recall_offset.z = GET_INT_ARG(1); + break; + + case CMD_MODEL_HITENEMY: // Flag to determine if an enemy projectile will hit enemies + value = GET_ARG(1); + if(atoi(value) == 1) + { + newchar->faction.type_damage_direct = newchar->faction.type_hostile = TYPE_PLAYER | TYPE_ENEMY; + } + else if(atoi(value) == 2) + { + newchar->faction.type_damage_direct = newchar->faction.type_hostile = TYPE_PLAYER; + } + + newchar->ground = GET_INT_ARG(2); // Added to determine if enemies are damaged with mid air projectiles or ground only + break; + + /* Faction set up. */ + case CMD_MODEL_FACTION_GROUP_DAMAGE_DIRECT: + newchar->faction.damage_direct = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_MODEL_FACTION_GROUP_DAMAGE_INDIRECT: + newchar->faction.damage_indirect = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_MODEL_FACTION_GROUP_HOSTILE: + newchar->faction.hostile = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_MODEL_FACTION_GROUP_MEMBER: + newchar->faction.member = faction_get_flags_from_command_line(buf + pos); + break; + + /* Legacy type based faction */ + case CMD_MODEL_FACTION_TYPE_HOSTILE: + case CMD_MODEL_HOSTILE: + newchar->faction.type_hostile = get_type_from_command_line(buf + pos); + break; + + case CMD_MODEL_FACTION_TYPE_DAMAGE_DIRECT: + case CMD_MODEL_CANDAMAGE: + newchar->faction.type_damage_direct = get_type_from_command_line(buf + pos); + break; + + case CMD_MODEL_FACTION_TYPE_DAMAGE_INDIRECT: + case CMD_MODEL_PROJECTILEHIT: + newchar->faction.type_damage_indirect = get_type_from_command_line(buf + pos); + break; + + case CMD_MODEL_AIMOVE: + newchar->aimove = get_aimove_from_command_line(buf + pos, AIMOVE1_NORMAL); + break; + case CMD_MODEL_AIATTACK: + lcmHandleCommandAiattack(&arglist, newchar, &aiattackset, filename); + break; + case CMD_MODEL_MOVE_CONFIG: + newchar->move_config_flags = get_move_config_flags_from_command_line(buf + pos); + break; + case CMD_MODEL_SUBJECT_TO_BASEMAP: + + /* Legacy code allowed -1 or 0 for False. */ + if (GET_INT_ARG(1) > 0) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_BASEMAP; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_BASEMAP; + } + + break; + case CMD_MODEL_SUBJECT_TO_WALL: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_WALL; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_WALL; + } + + break; + case CMD_MODEL_SUBJECT_TO_HOLE: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_HOLE; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_HOLE; + } + + break; + case CMD_MODEL_SUBJECT_TO_PLATFORM: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_PLATFORM; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_PLATFORM; + } + + break; + case CMD_MODEL_SUBJECT_TO_OBSTACLE: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_OBSTACLE; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_OBSTACLE; + } + + break; + case CMD_MODEL_SUBJECT_TO_GRAVITY: + + /* Legacy code allowed -1 or 0 for False. */ + if (GET_INT_ARG(1) > 0) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + + break; + case CMD_MODEL_SUBJECT_TO_SCREEN: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_SCREEN; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_SCREEN; + } + + break; + case CMD_MODEL_SUBJECT_TO_MINZ: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_MIN_Z; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_MIN_Z; + } + + break; + case CMD_MODEL_SUBJECT_TO_MAXZ: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_MAX_Z; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_MAX_Z; + } + + break; + case CMD_MODEL_NO_ADJUST_BASE: + + /* Legacy code allowed -1 or 0 for False. */ + if (GET_INT_ARG(1) > 0) + { + newchar->move_config_flags |= MOVE_CONFIG_NO_ADJUST_BASE; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + } + + break; + case CMD_MODEL_INSTANTITEMDEATH: + newchar->instantitemdeath = GET_INT_ARG(1); + break; + case CMD_MODEL_SECRET: + newchar->secret = GET_INT_ARG(1); + newchar->clearcount = GET_INT_ARG(2); + break; + case CMD_MODEL_MODELFLAG: // Legacy model copy flag. + + + lcmHandleCommandModelFlag(filename, command, buf + pos, newchar); + + break; + // weapons + case CMD_MODEL_WEAPLOSS: + + /* Legacy weapon loss. */ + + newchar->weapon_properties.loss_condition = weapon_loss_condition_interpret_from_legacy_weaploss(WEAPON_LOSS_CONDITION_NONE, GET_INT_ARG(1)); + newchar->weapon_properties.loss_index = GET_INT_ARG(2); + break; + + case CMD_MODEL_WEAPON_LOSS_CONFIG: + + lcmHandleCommandWeaponLossCondition(buf + pos, newchar); + break; + + case CMD_MODEL_WEAPON_LOSS_INDEX: + + /* Weapon list entry we revert to when losing weapon. */ + + newchar->weapon_properties.loss_index = GET_INT_ARG(1); + + break; + + case CMD_MODEL_WEAPNUM: + newchar->weapon_properties.weapon_index = GET_INT_ARG(1); + break; + case CMD_MODEL_PROJECT: // New projectile subtype + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->project = MODEL_INDEX_NONE; + } + else + { + newchar->project = get_cached_model_index(value); + } + break; + case CMD_MODEL_WEAPONS: + lcmHandleCommandWeapons(buf + pos, newchar); + break; + case CMD_MODEL_SHOOTNUM: + newchar->weapon_properties.use_count = GET_INT_ARG(1); + break; + case CMD_MODEL_RELOAD: + newchar->weapon_properties.use_add = GET_INT_ARG(1); + break; + case CMD_MODEL_TYPESHOT: + if (GET_INT_ARG(1)) + { + newchar->weapon_properties.weapon_state |= WEAPON_STATE_LIMITED_USE; + } + + break; + case CMD_MODEL_COUNTER: + newchar->weapon_properties.loss_count = GET_INT_ARG(1); + break; + case CMD_MODEL_ANIMAL: + if (GET_INT_ARG(1)) + { + newchar->weapon_properties.weapon_state |= WEAPON_STATE_ANIMAL; + } + break; + case CMD_MODEL_RIDER: + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->rider = MODEL_INDEX_NONE; + } + else + { + newchar->rider = get_cached_model_index(value); + } + break; + case CMD_MODEL_KNIFE: + case CMD_MODEL_FIREB: + case CMD_MODEL_PLAYSHOT: + case CMD_MODEL_PLAYSHOTW: + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->knife = MODEL_INDEX_NONE; + } + else + { + newchar->knife = get_cached_model_index(value); + } + break; + case CMD_MODEL_PLAYSHOTNO: + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->pshotno = MODEL_INDEX_NONE; + } + else + { + newchar->pshotno = get_cached_model_index(value); + } + break; + case CMD_MODEL_STAR: + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->star = MODEL_INDEX_NONE; + } + else + { + newchar->star = get_cached_model_index(value); + } + break; + case CMD_MODEL_BOMB: + case CMD_MODEL_PLAYBOMB: + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->bomb = MODEL_INDEX_NONE; + } + else + { + newchar->bomb = get_cached_model_index(value); + } + break; + case CMD_MODEL_FLASH: // Now all characters can have their own flash - even projectiles (useful for blood) + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->flash.model_hit = MODEL_INDEX_NONE; + } + else + { + newchar->flash.model_hit = get_cached_model_index(value); + } + break; + case CMD_MODEL_BFLASH: // Flash that is spawned if an attack is blocked + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->flash.model_block = MODEL_INDEX_NONE; + } + else + { + newchar->flash.model_block = get_cached_model_index(value); + } + break; + case CMD_MODEL_DUST: // Spawned when hitting the ground to "kick up dust" + value = GET_ARG(1); + if(stricmp(value, "none") == 0) + { + newchar->dust.fall_land = MODEL_INDEX_NONE; + } + else + { + newchar->dust.fall_land = get_cached_model_index(value); + } + value = GET_ARG(2); + if(stricmp(value, "none") == 0) + { + newchar->dust.jump_land = MODEL_INDEX_NONE; + } + else + { + newchar->dust.jump_land = get_cached_model_index(value); + } + value = GET_ARG(3); + if(stricmp(value, "none") == 0) + { + newchar->dust.jump_start = MODEL_INDEX_NONE; + } + else + { + newchar->dust.jump_start = get_cached_model_index(value); + } + break; + case CMD_MODEL_BRANCH: // for endlevel item's level branch + value = GET_ARG(1); + if(!newchar->branch) + { + newchar->branch = malloc(MAX_NAME_LEN + 1); + newchar->branch[0] = 0; + } + strncpy(newchar->branch, value, MAX_NAME_LEN); + break; + case CMD_MODEL_CANTGRAB: + case CMD_MODEL_NOTGRAB: + tempInt = GET_INT_ARG(1); + if(tempInt == 2) + { + newchar->grab_force = -999999; + } + else + { + newchar->grab_resistance = 1; + } + break; + case CMD_MODEL_ANTIGRAB: // a can grab b: a->grab_resistance - b->grab_force <=0 + newchar->grab_resistance = GET_INT_ARG(1); + break; + case CMD_MODEL_GRABFORCE: + newchar->grab_force = GET_INT_ARG(1); + break; + case CMD_MODEL_GRABBACK: + newchar->grabback = GET_INT_ARG(1); + break; + case CMD_MODEL_OFFSCREENKILL: + newchar->offscreenkill = GET_INT_ARG(1); + break; + case CMD_MODEL_ONAF: + newchar->offscreen_noatk_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_FALLDIE: + case CMD_MODEL_DEATH: + + tempInt = GET_INT_ARG(1); + + newchar->death_config_flags = death_config_get_value_from_falldie(newchar->death_config_flags, tempInt); + + break; + + case CMD_MODEL_DEATH_CONFIG: + + newchar->death_config_flags = death_get_config_flags_from_command_line(buf + pos, 1); + break; + + case CMD_MODEL_SPEED: + value = GET_ARG(1); + newchar->speed.x = atof(value); + newchar->speed.x /= 10; + if(newchar->speed.x < 0.5) + { + newchar->speed.x = 0.5; + } + if(newchar->speed.x > 30) + { + newchar->speed.x = 30; + } + break; + case CMD_MODEL_SPEEDF: + value = GET_ARG(1); + newchar->speed.x = atof(value); + break; + case CMD_MODEL_JUMPSPECIAL: // Kratus (10-2021) Added new jumpspecial property + newchar->jumpspecial = GET_INT_ARG(1); + break; + case CMD_MODEL_JUMPSPEED: + value = GET_ARG(1); + newchar->jumpspeed = atof(value); + newchar->jumpspeed /= 10; + break; + case CMD_MODEL_JUMPSPEEDF: + value = GET_ARG(1); + newchar->jumpspeed = atof(value); + break; + case CMD_MODEL_ANTIGRAVITY: + value = GET_ARG(1); + newchar->antigravity = atof(value); + newchar->antigravity /= 100; + break; + case CMD_MODEL_STEALTH: + newchar->stealth.hide = GET_INT_ARG(1); + newchar->stealth.detect = GET_INT_ARG(2); + break; + case CMD_MODEL_JUGGLEPOINTS: + value = GET_ARG(1); + newchar->jugglepoints = atoi(value); + break; + case CMD_MODEL_RISEATTACKTYPE: + value = GET_ARG(1); + newchar->riseattacktype = atoi(value); + break; + case CMD_MODEL_GUARDPOINTS: + value = GET_ARG(1); + newchar->guardpoints = atoi(value); + break; + case CMD_MODEL_DEFENSE: + + /* + * DEFENSE_PARAMETER_LEGACY triggers muti-parameter read + * from 3.0 builds. See function for details. + */ + + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_LEGACY); + break; + case CMD_MODEL_DEFENSE_BLOCK_DAMAGE_ADJUST: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_DAMAGE_ADJUST); + break; + case CMD_MODEL_DEFENSE_BLOCK_DAMAGE_MAX: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_DAMAGE_MAX); + break; + case CMD_MODEL_DEFENSE_BLOCK_DAMAGE_MIN: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_DAMAGE_MIN); + break; + case CMD_MODEL_DEFENSE_BLOCK_POWER: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_POWER); + break; + case CMD_MODEL_DEFENSE_BLOCK_RATIO: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_RATIO); + break; + case CMD_MODEL_DEFENSE_BLOCK_THRESHOLD: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_THRESHOLD); + break; + case CMD_MODEL_DEFENSE_BLOCK_TYPE: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_BLOCK_TYPE); + break; + case CMD_MODEL_DEFENSE_DAMAGE_ADJUST: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_DAMAGE_ADJUST); + break; + case CMD_MODEL_DEFENSE_DAMAGE_MAX: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_DAMAGE_MAX); + break; + case CMD_MODEL_DEFENSE_DAMAGE_MIN: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_DAMAGE_MIN); + break; + case CMD_MODEL_DEFENSE_DEATH_CONFIG: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_DEATH_CONFIG); + break; + case CMD_MODEL_DEFENSE_FACTOR: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_FACTOR); + break; + case CMD_MODEL_DEFENSE_KNOCKDOWN: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_KNOCKDOWN); + break; + case CMD_MODEL_DEFENSE_PAIN: + defense_setup_from_arg(filename, command, buf + pos, newchar->defense, &arglist, DEFENSE_PARAMETER_PAIN); + break; + case CMD_MODEL_OFFENSE: + offense_setup_from_arg(filename, command, newchar->offense, &arglist, OFFENSE_PARAMETER_LEGACY); + break; + case CMD_MODEL_OFFENSE_DAMAGE_ADJUST: + offense_setup_from_arg(filename, command, newchar->offense, &arglist, OFFENSE_PARAMETER_DAMAGE_ADJUST); + break; + case CMD_MODEL_OFFENSE_DAMAGE_MAX: + offense_setup_from_arg(filename, command, newchar->offense, &arglist, OFFENSE_PARAMETER_DAMAGE_MAX); + break; + case CMD_MODEL_OFFENSE_DAMAGE_MIN: + offense_setup_from_arg(filename, command, newchar->offense, &arglist, OFFENSE_PARAMETER_DAMAGE_MIN); + break; + case CMD_MODEL_OFFENSE_FACTOR: + offense_setup_from_arg(filename, command, newchar->offense, &arglist, OFFENSE_PARAMETER_FACTOR); + break; + + case CMD_MODEL_HEIGHT: + newchar->size.y = GET_INT_ARG(1); + break; + case CMD_MODEL_JUMPHEIGHT: + newchar->jumpheight = GET_FLOAT_ARG(1); + break; + + case CMD_MODEL_AIR_CONTROL: + + lcmHandleCommandAirControl(buf + pos, newchar); + break; + + case CMD_MODEL_JUMPMOVE: + + newchar->air_control = air_control_interpret_from_legacy_jumpmove_x(newchar->air_control, GET_INT_ARG(1)); + newchar->air_control = air_control_interpret_from_legacy_jumpmove_z(newchar->air_control, GET_INT_ARG(2)); + break; + + case CMD_MODEL_WALKOFFMOVE: + + newchar->air_control = air_control_interpret_from_legacy_walkoffmove_x(newchar->air_control, GET_INT_ARG(1)); + newchar->air_control = air_control_interpret_from_legacy_walkoffmove_z(newchar->air_control, GET_INT_ARG(2));; + break; + + case CMD_MODEL_KNOCKDOWNCOUNT: + newchar->knockdowncount = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_GRABDISTANCE: + newchar->grabdistance = GET_FLOAT_ARG(1); // 30-12-2004 and store for character + break; + case CMD_MODEL_GRABFLIP: + newchar->grabflip = GET_INT_ARG(1); + break; + case CMD_MODEL_GRABFINISH: + newchar->grabfinish = GET_INT_ARG(1); + break; + case CMD_MODEL_THROWDAMAGE: + newchar->throwdamage = GET_INT_ARG(1); + break; + case CMD_MODEL_SHADOW: + newchar->shadow = GET_INT_ARG(1); + + tempInt= GET_INT_ARG(2); + + newchar->shadow_config_flags = shadow_get_config_from_legacy_shadowbase(newchar->shadow_config_flags, tempInt); + + break; + case CMD_MODEL_SHADOW_CONFIG: + + newchar->shadow_config_flags = shadow_get_config_flags_from_command_line(buf + pos); + + break; + case CMD_MODEL_GFXSHADOW: + + /* Gfxshadow. */ + tempInt = GET_INT_ARG(1); + newchar->shadow_config_flags = shadow_get_config_from_legacy_gfxshadow(newchar->shadow_config_flags, tempInt); + + /* Shadowbase. */ + tempInt = GET_INT_ARG(2); + newchar->shadow_config_flags = shadow_get_config_from_legacy_shadowbase(newchar->shadow_config_flags, tempInt); + + break; + + case CMD_MODEL_AIRONLY: // Shadows display in air only? + + tempInt = GET_INT_ARG(1); + + newchar->shadow_config_flags = shadow_get_config_from_legacy_aironly(newchar->shadow_config_flags, tempInt); + + break; + case CMD_MODEL_FMAP: // Map that corresponds with the remap when a character is frozen + newchar->colorsets.frozen = GET_INT_ARG(1); + break; + case CMD_MODEL_KOMAP: + newchar->colorsets.ko = GET_INT_ARG(1); //Remap. + newchar->colorsets.kotype = komap_type_get_value_from_argument(filename, command, GET_ARG(2)); + break; + case CMD_MODEL_MAP_BURN_INDEX: + newchar->colorsets.burn = GET_INT_ARG(1); + break; + case CMD_MODEL_MAP_KO_INDEX: + newchar->colorsets.ko = GET_INT_ARG(1); + break; + case CMD_MODEL_MAP_KO_TYPE: + newchar->colorsets.kotype = komap_type_get_value_from_argument(filename, command, GET_ARG(1)); + break; + case CMD_MODEL_MAP_FREEZE_INDEX: + newchar->colorsets.frozen = GET_INT_ARG(1); + break; + case CMD_MODEL_MAP_SHOCK_INDEX: + newchar->colorsets.shock = GET_INT_ARG(1); + break; + case CMD_MODEL_HMAP: // Maps range unavailable to player in select screen. + newchar->colorsets.hide_start = GET_INT_ARG(1); //First unavailable map. + newchar->colorsets.hide_end = GET_INT_ARG(2); //Last unavailable map. + break; + case CMD_MODEL_SETLAYER: + newchar->setlayer = GET_INT_ARG(1); + break; + case CMD_MODEL_TOFLIP: // Flag to determine if flashes images will be flipped or not + newchar->toflip = GET_INT_ARG(1); + break; + case CMD_MODEL_NODIEBLINK: + // Added to determine if dying animation blinks or not + + tempInt = GET_INT_ARG(1); + + newchar->death_config_flags = death_config_get_value_from_nodieblink(newchar->death_config_flags, tempInt); + + break; + case CMD_MODEL_NOATFLASH: // Flag to determine if an opponents attack spawns their flash or not + newchar->noatflash = GET_INT_ARG(1); + break; + case CMD_MODEL_NOMOVE: + + /* + * Legacy no move. Set move constraint flags + * to match the old nomove parameters. + */ + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_NO_MOVE; + newchar->pain_config_flags |= PAIN_CONFIG_FALL_DISABLE; + } + + if (GET_INT_ARG(2)) + { + newchar->move_config_flags |= MOVE_CONFIG_NO_FLIP; + } + + break; + case CMD_MODEL_NODROP: + + tempInt = GET_INT_ARG(1); + + newchar->pain_config_flags &= ~(PAIN_CONFIG_FALL_DISABLE | PAIN_CONFIG_FALL_DISABLE_AIR); + + switch (tempInt) + { + default: + case 0: + break; + case 1: + newchar->pain_config_flags |= PAIN_CONFIG_FALL_DISABLE; + break; + case 2: + newchar->pain_config_flags |= (PAIN_CONFIG_FALL_DISABLE | PAIN_CONFIG_FALL_DISABLE_AIR); + break; + } + + break; + case CMD_MODEL_THOLD: + // Threshold for enemies/players block + newchar->thold = GET_INT_ARG(1); + break; + + case CMD_MODEL_RUN_CONFIG: + newchar->run_config_flags = run_get_config_flags_from_command_line(buf + pos, 1); + break; + case CMD_MODEL_RUNNING: + // The speed at which the player runs + newchar->runspeed = GET_FLOAT_ARG(1); + newchar->runspeed /= 10; + newchar->runjumpheight = GET_FLOAT_ARG(2); // The height at which a player jumps when running + newchar->runjumpdist = GET_FLOAT_ARG(3); // The distance a player jumps when running + + tempInt = GET_INT_ARG(4); + + newchar->run_config_flags |= (RUN_CONFIG_X_LEFT_ENABLED | RUN_CONFIG_X_LEFT_INITIAL | RUN_CONFIG_X_RIGHT_ENABLED | RUN_CONFIG_X_RIGHT_INITIAL); + + if (tempInt) + { + newchar->run_config_flags |= (RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_UP_ENABLED); + } + else + { + newchar->run_config_flags &= ~(RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_UP_ENABLED); + } + + tempInt = GET_INT_ARG(5); + + if (tempInt) + { + newchar->run_config_flags |= RUN_CONFIG_LAND; + } + else + { + newchar->run_config_flags &= ~RUN_CONFIG_LAND; + } + + break; + case CMD_MODEL_RUNNING_CONTINUE: + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->run_config_flags |= RUN_CONFIG_LAND; + } + else + { + newchar->run_config_flags &= ~RUN_CONFIG_LAND; + } + break; + case CMD_MODEL_RUNNING_JUMP_VELOCITY_X: + newchar->runjumpdist = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_RUNNING_JUMP_VELOCITY_Y: + newchar->runjumpheight = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_RUNNING_SPEED: + newchar->runspeed = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_RUNNING_Z_MOVE: + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->run_config_flags |= (RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_UP_ENABLED); + } + else + { + newchar->run_config_flags &= ~(RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_UP_ENABLED); + } + + break; + case CMD_MODEL_BLOCKODDS: + // Odds that an attack will hit an enemy (1 : blockodds) + newchar->blockodds = GET_INT_ARG(1); + break; + case CMD_MODEL_HOLDBLOCK: + + tempInt = GET_INT_ARG(1); + + newchar->block_config_flags &= ~(BLOCK_CONFIG_HOLD_IMPACT | BLOCK_CONFIG_HOLD_INFINITE); + + switch (tempInt) + { + default: + case 0: + /* Do nothing. */ + break; + case 1: + newchar->block_config_flags |= BLOCK_CONFIG_HOLD_IMPACT; + break; + case 2: + newchar->block_config_flags |= (BLOCK_CONFIG_HOLD_IMPACT | BLOCK_CONFIG_HOLD_INFINITE); + break; + } + + break; + case CMD_MODEL_BLOCKPAIN: + newchar->blockpain = GET_INT_ARG(1); + break; + case CMD_MODEL_NOPASSIVEBLOCK: + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->block_config_flags |= BLOCK_CONFIG_ACTIVE; + } + else + { + newchar->block_config_flags &= ~BLOCK_CONFIG_ACTIVE; + } + + break; + case CMD_MODEL_EDELAY: + tempInt = GET_INT_ARG(1); + + if (tempInt == EDELAY_MODE_MULTIPLY) + { + newchar->edelay.factor = GET_FLOAT_ARG(2); + } + else + { + newchar->edelay.modifier = GET_INT_ARG(2); + } + + newchar->edelay.cap.min = GET_INT_ARG(3); + newchar->edelay.cap.max = GET_INT_ARG(4); + newchar->edelay.range.min = GET_INT_ARG(5); + newchar->edelay.range.max = GET_INT_ARG(6); + break; + + case CMD_MODEL_ENHANCED_DELAY_CAP_MAX: + + newchar->edelay.cap.max = GET_INT_ARG(1); + break; + + case CMD_MODEL_ENHANCED_DELAY_CAP_MIN: + + newchar->edelay.cap.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_ENHANCED_DELAY_MODIFIER: + + newchar->edelay.modifier = GET_INT_ARG(1); + break; + + case CMD_MODEL_ENHANCED_DELAY_MULTIPLIER: + + newchar->edelay.factor = GET_FLOAT_ARG(1); + break; + + case CMD_MODEL_ENHANCED_DELAY_RANGE_MAX: + + newchar->edelay.range.max = GET_INT_ARG(1); + break; + + case CMD_MODEL_ENHANCED_DELAY_RANGE_MIN: + + newchar->edelay.range.min = GET_INT_ARG(1); + break; + + case CMD_MODEL_PAIN_BACK: + + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->pain_config_flags |= PAIN_CONFIG_BACK_PAIN; + } + else + { + newchar->pain_config_flags &= ~PAIN_CONFIG_BACK_PAIN; + } + + break; + case CMD_MODEL_PAIN_CONFIG: + + tempInt = GET_INT_ARG(1); + + newchar->pain_config_flags = pain_get_config_flags_from_command_line(buf + pos); + + break; + + case CMD_MODEL_PAINGRAB: + newchar->paingrab = GET_INT_ARG(1); + break; + case CMD_MODEL_THROW: + newchar->throwdist = GET_FLOAT_ARG(1); + newchar->throwheight = GET_FLOAT_ARG(2); + break; + case CMD_MODEL_EDGERANGE: + newchar->edgerange.x = GET_FLOAT_ARG(1); + newchar->edgerange.z = GET_FLOAT_ARG(2); + break; + case CMD_MODEL_ENTITYPUSHING: + newchar->entitypushing = GET_INT_ARG(1); + break; + case CMD_MODEL_PUSHINGFACTOR: + newchar->pushingfactor = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_GRABWALK: + newchar->grabwalkspeed = GET_FLOAT_ARG(1); + newchar->grabwalkspeed /= 10; + if(newchar->grabwalkspeed < 0.5) + { + newchar->grabwalkspeed = 0.5; + } + break; + case CMD_MODEL_GRABTURN: + newchar->grabturn = GET_INT_ARG(1); + break; + case CMD_MODEL_THROWFRAMEWAIT: + newchar->throwframewait = GET_INT_ARG(1); + break; + case CMD_MODEL_DIESOUND: + newchar->diesound = sound_load_sample(GET_ARG(1), packfile, 1, 0); + break; + case CMD_MODEL_ICON: + value = GET_ARG(1); + if(newchar->icon.def > -1) + { + shutdownmessage = "model has multiple icons defined"; + goto lCleanup; + } + newchar->icon.def = loadsprite(value, 0, 0, pixelformat); //use same palette as the owner + newchar->icon.pain = newchar->icon.def; + newchar->icon.die = newchar->icon.def; + newchar->icon.get = newchar->icon.def; + newchar->icon.usemap = GET_INT_ARG(2); //be more friendly to some old mods which don't care about icon remap + break; + case CMD_MODEL_ICONPAIN: + value = GET_ARG(1); + newchar->icon.pain = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONDIE: + value = GET_ARG(1); + newchar->icon.die = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONGET: + value = GET_ARG(1); + newchar->icon.get = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONW: + value = GET_ARG(1); + newchar->icon.weapon = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONMPHALF: + value = GET_ARG(1); + newchar->icon.mpmed = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONMPHIGH: + value = GET_ARG(1); + newchar->icon.mphigh = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONMPLOW: + value = GET_ARG(1); + newchar->icon.mplow = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONMPMAX: + value = GET_ARG(1); + newchar->icon.mpmax = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_ICONMPNONE: + value = GET_ARG(1); + newchar->icon.mpnone = loadsprite(value, 0, 0, pixelformat); + break; + case CMD_MODEL_PARROW: + // Image that is displayed when player 1 spawns invincible + value = GET_ARG(1); + newchar->player_arrow[0].sprite = loadsprite(value, 0, 0, pixelformat); + newchar->player_arrow[0].position.x = GET_INT_ARG(2); + newchar->player_arrow[0].position.y = GET_INT_ARG(3); + break; + case CMD_MODEL_PARROW2: + // Image that is displayed when player 2 spawns invincible + value = GET_ARG(1); + newchar->player_arrow[1].sprite = loadsprite(value, 0, 0, pixelformat); + newchar->player_arrow[1].position.x = GET_INT_ARG(2); + newchar->player_arrow[1].position.y = GET_INT_ARG(3); + break; + case CMD_MODEL_PARROW3: + value = GET_ARG(1); + newchar->player_arrow[2].sprite = loadsprite(value, 0, 0, pixelformat); + newchar->player_arrow[2].position.x = GET_INT_ARG(2); + newchar->player_arrow[2].position.y = GET_INT_ARG(3); + break; + case CMD_MODEL_PARROW4: + value = GET_ARG(1); + newchar->player_arrow[3].sprite = loadsprite(value, 0, 0, pixelformat); + newchar->player_arrow[3].position.x = GET_INT_ARG(2); + newchar->player_arrow[3].position.y = GET_INT_ARG(3); + break; + case CMD_MODEL_ATCHAIN: + newchar->chainlength = 0; + for(i = 0; i < MAX_ATCHAIN; i++) + { + newchar->atchain[i] = GET_INT_ARG(i + 1); + if(newchar->atchain[i] < 0) + { + newchar->atchain[i] = 0; + } + if(newchar->atchain[i] > max_attacks) + { + newchar->atchain[i] = max_attacks; + } + if(newchar->atchain[i]) + { + newchar->chainlength = i + 1; + } + } + break; + case CMD_MODEL_COMBOSTYLE: + newchar->combostyle = GET_INT_ARG(1); + break; + case CMD_MODEL_CREDIT: + newchar->credit = GET_INT_ARG(1); + break; + case CMD_MODEL_NOPAIN: + tempInt = GET_INT_ARG(1); + + if (tempInt) + { + newchar->pain_config_flags |= PAIN_CONFIG_PAIN_DISABLE; + } + else + { + newchar->pain_config_flags &= ~PAIN_CONFIG_PAIN_DISABLE; + } + + break; + case CMD_MODEL_ESCAPEHITS: + // How many times an enemy can be hit before retaliating + newchar->escapehits = GET_INT_ARG(1); + break; + case CMD_MODEL_CHARGERATE: + // How much mp does this character gain while recharging? + newchar->chargerate = GET_INT_ARG(1); + break; + case CMD_MODEL_MPRATE: + newchar->mprate = GET_INT_ARG(1); + break; + case CMD_MODEL_MPSET: + // Mp bar wax/wane. + newchar->mp = GET_INT_ARG(1); //Max MP. + newchar->mpstable = GET_INT_ARG(2); //MP stable setting. + newchar->mpstableval = GET_INT_ARG(3); //MP stable value (% Mp bar will try and maintain). + newchar->mprate = GET_INT_ARG(4); //Rate MP value rises over time. + newchar->mpdroprate = GET_INT_ARG(5); //Rate MP value drops over time. + newchar->chargerate = GET_INT_ARG(6); //MP Chargerate. + break; + case CMD_MODEL_SLEEPWAIT: + newchar->sleepwait = GET_INT_ARG(1); + break; + case CMD_MODEL_GUARDRATE: + newchar->guardrate = GET_INT_ARG(1); + break; + case CMD_MODEL_AGGRESSION: + newchar->aggression = GET_INT_ARG(1); + break; + case CMD_MODEL_ATTACKTHROTTLE: + newchar->attackthrottle = GET_FLOAT_ARG(1); + if(arglist.count >= 2) + { + newchar->attackthrottletime = GET_FLOAT_ARG(2) * global_config.game_speed; + } + break; + case CMD_MODEL_RISETIME: + newchar->risetime.rise = GET_INT_ARG(1); + newchar->risetime.riseattack = GET_INT_ARG(2); + break; + case CMD_MODEL_FACING: + newchar->facing = GET_INT_ARG(1); + break; + case CMD_MODEL_TURNDELAY: + newchar->turndelay = GET_INT_ARG(1); + break; + case CMD_MODEL_LIFESPAN: + newchar->lifespan = GET_FLOAT_ARG(1) * global_config.game_speed; + break; + case CMD_MODEL_SUMMONKILL: + newchar->summonkill = GET_INT_ARG(1); + break; + case CMD_MODEL_LIFEPOSITION: + + /* + * Allocate object if we need it first. + */ + + if (!newchar->hud_popup) + { + newchar->hud_popup = bar_status_allocate_object(); + } + + if ((value = GET_ARG(1))[0]) + { + newchar->hud_popup->graph_position.x = atoi(value); + } + if ((value = GET_ARG(2))[0]) + { + newchar->hud_popup->graph_position.y = atoi(value); + } + + break; + case CMD_MODEL_LIFEBARSTATUS: + + /* + * Allocate object if we need it first. + */ + + if (!newchar->hud_popup) + { + newchar->hud_popup = bar_status_allocate_object(); + } + + _readbarstatus(buf + pos, newchar->hud_popup); + newchar->hud_popup->colourtable = &hpcolourtable; + break; + case CMD_MODEL_ICONPOSITION: + if((value = GET_ARG(1))[0]) + { + newchar->icon.position.x = atoi(value); + } + if((value = GET_ARG(2))[0]) + { + newchar->icon.position.y = atoi(value); + } + break; + case CMD_MODEL_NAMEPOSITION: + + /* + * Allocate object if we need it first. + */ + + if (!newchar->hud_popup) + { + newchar->hud_popup = bar_status_allocate_object(); + } + + if((value = GET_ARG(1))[0]) + { + newchar->hud_popup->name_position.x = atoi(value); + } + if((value = GET_ARG(2))[0]) + { + newchar->hud_popup->name_position.y = atoi(value); + } + break; + + case CMD_MODEL_COM: + { + s_command_token_reader token_reader = { + .cursor = buf + pos + }; + + s_command_token token; + + /* + * Parse into a temporary command first. This prevents + * a malformed line from partially modifying the model. + */ + s_com parsed_special = {0}; + + const char* parse_error; + + int freespecial_number; + + /* + * Consume and verify the command-name token. + */ + if(!command_token_reader_next(&token_reader, &token) || !command_token_equals(&token, "com")) { + shutdownmessage = "Invalid freespecial command name.\n"; + goto lCleanup; + } + + /* + * Parse all sequence tokens through the destination + * freespecial animation. + */ + parse_error = special_command_parse_sequence( + &token_reader, + &parsed_special, + &freespecial_number, + alert_buffer, + sizeof(alert_buffer) + ); + + if(parse_error) { + shutdownmessage = parse_error; + goto lCleanup; + } + + parsed_special.anim = animspecials[freespecial_number - 1]; + + if(!alloc_specials(newchar)) { + shutdownmessage = E_OUT_OF_MEMORY; + goto lCleanup; + } + + newchar->special[newchar->specials_loaded] = parsed_special; + newchar->specials_loaded++; + } + break; + + case CMD_MODEL_REMAP: + { + // This command should not be used under 24bit mode, but for old mods, just give it a default palette + value = GET_ARG(1); + value2 = GET_ARG(2); + __realloc(mapflag, newchar->maps_loaded); + errorVal = load_colourmap(newchar, value, value2); + + if(0 >= errorVal) + { + switch(errorVal) + { + case 0: // uhm wait, we just tested for !errorVal... + value2 = "Failed to create colourmap. Image Used Twice!"; + break; + case -1: //should not happen now + value2 = "Failed to create colourmap. Color maps full error (color maps are unlimited by engine - check memory limits of console)!"; + break; + case -2: + value2 = "Failed to create colourmap. Failed to allocate memory!"; + break; + case -3: + value2 = "Failed to create colourmap. Failed to load file 1"; + break; + case -4: + value2 = "Failed to create colourmap. Failed to load file 2"; + break; + } + printf("Warning: %s\n", value2); + } + else + { + if(pixelformat == PIXEL_x8 && newchar->palette == NULL) + { + newchar->palette = malloc(PAL_BYTES); + if(loadimagepalette(value, packfile, newchar->palette) == 0) + { + shutdownmessage = "Failed to load palette!"; + goto lCleanup; + } + } + mapflag[newchar->maps_loaded - 1] = 1; + } + } + break; + case CMD_MODEL_PALETTE: + + if(newchar->palette == NULL) + { + + // Command title for log. Details will be added below accordingly. + // Forced character length is to line up with Alternatepal logs. + //printf("\t\t\tPalette: \t"); + + // Get argument. + value = GET_ARG(1); + + // If "none" then set nopalette, meaning each + // frame retains its own color table as is. + // Otherwise we will set up the model level + // color table. + if(stricmp(value, "none") == 0) + { + nopalette = 1; + + //printf("%s\n", "'None' option active. All sprites for this model will be loaded with independent color tables."); + } + else + { + // Set up the model level color table. + // We can load either directly from + // an .act file or read the color table + // from an image. + + // Allocate space for the color table. + newchar->palette = malloc(PAL_BYTES); + + if(load_palette(newchar->palette, value) == 0) + { + printf("\n%s%s\n", "Failed to load color table from file: ", value); + shutdownmessage = "Failed to load color table from file."; + goto lCleanup; + } + + //printf("%s%s\n", "Loaded color selection 0: ", value); + } + } + + break; + case CMD_MODEL_ALTERNATEPAL: + + // Command title for log. Details will be added blow accordingly. + //printf("\t"LOG_CMD_TITLE"%s", "Alternatepal", " - "); + + __realloc(mapflag, newchar->maps_loaded); + __realloc(newchar->colourmap, newchar->maps_loaded); + value = GET_ARG(1); + + newchar->colourmap[newchar->maps_loaded] = malloc(PAL_BYTES); + + if(load_palette(newchar->colourmap[newchar->maps_loaded], value) == 0) + { + printf("\n%s%s\n", "Failed to load color table from file: ", value); + shutdownmessage = "Failed to load color table from file."; + goto lCleanup; + } + + newchar->maps_loaded++; + + //printf("Loaded color selection %i: %s", newchar->maps_loaded, value); + + break; + case CMD_MODEL_GLOBALMAP: + + // Command title for log. Details will be added blow accordingly. + //printf("\t"LOG_CMD_TITLE"%s", "Globalmap", " - "); + + // use global palette under 24bit mode, so some entity/panel/bg can still use palette feature, that saves some memory + newchar->globalmap = GET_INT_ARG(1); + + //printf("%i: %s\n", newchar->globalmap, value); + + break; + case CMD_MODEL_ALPHA: + newchar->alpha = GET_INT_ARG(1); + break; + case CMD_MODEL_REMOVE: + value = GET_ARG(1); + + newchar->remove_config = get_remove_config_from_string(value); + + break; + case CMD_MODEL_SCRIPT: + //load the update script + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->update_script, "updateentityscript", filename, 1, 0); + break; + case CMD_MODEL_THINKSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->think_script, "thinkscript", filename, 1, 0); + break; + case CMD_MODEL_TAKEDAMAGESCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->takedamage_script, "takedamagescript", filename, 1, 0); + break; + case CMD_MODEL_ON_BIND_UPDATE_OTHER_TO_SELF_SCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->on_bind_update_other_to_self_script, "on_bind_update_other_to_self_script", filename, 1, 0); + break; + case CMD_MODEL_ON_BIND_UPDATE_SELF_TO_OTHER_SCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->on_bind_update_self_to_other_script, "on_bind_update_self_to_other_script", filename, 1, 0); + break; + case CMD_MODEL_ONFALLSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onfall_script, "onfallscript", filename, 1, 0); + break; + case CMD_MODEL_ONPAINSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onpain_script, "onpainscript", filename, 1, 0); + break; + case CMD_MODEL_INHOLESCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->inhole_script, "inholescript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKSSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocks_script, "onblocksscript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKWSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockw_script, "onblockwscript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKPSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockp_script, "onblockpscript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKOSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocko_script, "onblockoscript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKZSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblockz_script, "onblockzscript", filename, 1, 0); + break; + case CMD_MODEL_ONBLOCKYSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onblocky_script, "onblockyscript", filename, 1, 0); + break; + case CMD_MODEL_ONMOVEXSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovex_script, "onmovexscript", filename, 1, 0); + break; + case CMD_MODEL_ONMOVEZSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovez_script, "onmovezscript", filename, 1, 0); + break; + case CMD_MODEL_ONMOVEYSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovea_script, "onmoveyscript", filename, 1, 0); + break; + case CMD_MODEL_ONDEATHSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondeath_script, "ondeathscript", filename, 1, 0); + break; + case CMD_MODEL_ONENTITYCOLLISIONSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onentitycollision_script, "onentitycollisionscript", filename, 1, 0); + break; + case CMD_MODEL_ONKILLSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onkill_script, "onkillscript", filename, 1, 0); + break; + case CMD_MODEL_MODELLOADSCRIPT: + if(Script_IsInitialized(model_cache[cacheindex].load_script)) + { + Script_Clear(model_cache[cacheindex].load_script, 1); + } + pos += lcmHandleCommandScripts( + &arglist, + buf + pos, + model_cache[cacheindex].load_script, + "modelloadscript", + filename, + 1, + 0 + ); + break; + case CMD_MODEL_MODELUNLOADSCRIPT: + if(Script_IsInitialized(model_cache[cacheindex].unload_script)) + { + Script_Clear(model_cache[cacheindex].unload_script, 1); + } + pos += lcmHandleCommandScripts( + &arglist, + buf + pos, + model_cache[cacheindex].unload_script, + "modelunloadscript", + filename, + 1, + 0 + ); + break; + case CMD_MODEL_DIDBLOCKSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didblock_script, "didblockscript", filename, 1, 0); + break; + case CMD_MODEL_ONDOATTACKSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondoattack_script, "ondoattackscript", filename, 1, 0); + break; + case CMD_MODEL_DIDHITSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->didhit_script, "didhitscript", filename, 1, 0); + break; + case CMD_MODEL_ONSPAWNSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onspawn_script, "onspawnscript", filename, 1, 0); + break; + case CMD_MODEL_ONMODELCOPYSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmodelcopy_script, "onmodelcopyscript", filename, 1, 0); + break; + case CMD_MODEL_ONDRAWSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondraw_script, "ondrawscript", filename, 1, 0); + break; + case CMD_MODEL_ANIMATIONSCRIPT: + //pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->animation_script, "animationscript", filename, 0, 0); + pos += lcmScriptCopyBuffer(&arglist, buf + pos, &animscriptbuf); + //dont compile, until at end of this function + break; + case CMD_MODEL_KEYSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->key_script, "entitykeyscript", filename, 1, 0); + break; + case CMD_MODEL_ANIM: + { + bool freespecial_recognized; + + int freespecial_number; + + s_command_token animation_name_token; + + const char* freespecial_error; + + value = GET_ARG(1); + + /* + * Parse numbered freespecial animations before any + * allocation or animation-table indexing takes place. + */ + animation_name_token.text = value; + animation_name_token.length = strlen(value); + + freespecial_error = command_token_get_freespecial_number( + &animation_name_token, + &freespecial_recognized, + &freespecial_number, + alert_buffer, + sizeof(alert_buffer) + ); + + if(freespecial_error) { + shutdownmessage = freespecial_error; + goto lCleanup; + } + + frameset = 0; + framecount = 0; + // Create new animation + newanim = alloc_anim(); + if(!newanim) + { + shutdownmessage = "Not enough memory for animations!"; + goto lCleanup; + } + newanim->model_index = newchar->index; + // Reset vars + curframe = 0; + at_cmd_mergeable = false; + + /* + * Caskey, Damon V. + * 2021-08-23 + * + * Prepare temporary lists for input. + */ + collision_collection_free(temp_collision_attack); + collision_collection_free(temp_collision_body); + collision_collection_free(temp_collision_space); + temp_collision_attack = NULL; + temp_collision_body = NULL; + temp_collision_space = NULL; + temp_collision_index = 0; + + frame_sound_collection_free(temp_frame_sound); + temp_frame_sound = NULL; + temp_frame_sound_index = 0; + + child_spawn_free_list(temp_child_spawn_head); + temp_child_spawn_head = NULL; + temp_child_spawn_index = 0; + + //printf("\n\n anim: %p", newanim); + //child_spawn_dump_list(temp_child_spawn_head); + + memset(&offset, 0, sizeof(offset)); + memset(shadow_coords, 0, sizeof(shadow_coords)); + memset(shadow_xz, 0, sizeof(shadow_xz)); + memset(platform, 0, sizeof(platform)); + + shadow_set = 0; + drawmethod = plainmethod; + idle = 0; + move.base = -1; + move.axis.x = 0; + move.axis.y = 0; + move.axis.z = 0; + frameshadow = FRAME_SHADOW_NONE; + + /* + * Other than Min X, default ranges are + * based on jump height or grabdistance. + * This is partialy for legacy compatability. + * It works well enough that beginners won't + * know the difference and advanced creators + * will always want to adjust for their specfic + * needs. + */ + + const int range_default_grabdistance = (int)newchar->grabdistance / 3; + const int range_default_jumpheight_max = (int)newchar->jumpheight * 20; + const int range_default_jumpheight_min = (int)newchar->jumpheight * 10; + + newanim->range = (s_range){ + .base = {.max = range_default_jumpheight_max, .min = -range_default_jumpheight_min }, + .x = {.max = range_default_jumpheight_max, .min = (newanim->range.x.min) ? newanim->range.x.min : -10 }, + .y = {.max = range_default_jumpheight_max, .min = -range_default_jumpheight_min }, + .z = {.max = range_default_grabdistance, .min = -range_default_grabdistance } + }; + + + newanim->energy_cost.cost = 0; + newanim->energy_cost.disable = 0; + newanim->energy_cost.mponly = COST_TYPE_MP_THEN_HP; + newanim->charge_time = ANIMATION_CHARGE_TIME_DEFAULT; + newanim->projectile = NULL; + newanim->flipframe = FRAME_NONE; + newanim->attack_one = 0; + newanim->move_config_flags |= MOVE_CONFIG_SUBJECT_TO_GRAVITY; + newanim->followup.animation = 0; // Default disabled + newanim->followup.condition = FOLLOW_CONDITION_NONE; + newanim->sub_entity_unsummon = FRAME_NONE; + newanim->landframe.frame = FRAME_NONE; + newanim->landframe.model_index = FRAME_SET_MODEL_INDEX_DEFAULT; + + newanim->jumpframe = (s_onframe_move){ + .frame = FRAME_NONE, + .model_index = MODEL_INDEX_NONE, + .velocity = default_model_dropv + }; + + newanim->dropframe.frame = FRAME_NONE; + newanim->dropframe.model_index = FRAME_SET_MODEL_INDEX_DEFAULT; + newanim->cancel = ANIMATION_CANCEL_DISABLED; // OX. For cancelling anims into a freespecial. + newanim->hit_count = 0; //OX counts hits on a per anim basis for cancels. + newanim->sub_entity_model_index = MODEL_INDEX_NONE; + newanim->sub_entity_spawn = NULL; + newanim->sub_entity_summon = NULL; + newanim->quakeframe.framestart = 0; + newanim->sync = FRAME_NONE; + + if(freespecial_recognized) { + ani_id = animspecials[freespecial_number - 1]; + + } else { + ani_id = translate_ani_id(value, newchar, newanim); + } + + if(ani_id < 0) + { + shutdownmessage = "Invalid animation name!"; + goto lCleanup; + } + + newchar->animation[ani_id] = newanim; + } + break; + case CMD_MODEL_LOOP: + if(!newanim) + { + shutdownmessage = "Can't set loop: no animation specified!"; + goto lCleanup; + } + newanim->loop.mode = GET_INT_ARG(1); //0 = Off, 1 = on. + newanim->loop.frame.min = GET_INT_ARG(2); //Loop to frame. + newanim->loop.frame.max = GET_INT_ARG(3); //Loop end frame. + break; + case CMD_MODEL_ANIMHEIGHT: + newanim->size.y = GET_INT_ARG(1); + break; + case CMD_MODEL_SYNC: + { + bool freespecial_recognized; + + int freespecial_number; + + s_command_token animation_name_token; + + const char* freespecial_error; + + value = GET_ARG(1); + + animation_name_token.text = value; + animation_name_token.length = strlen(value); + + freespecial_error = command_token_get_freespecial_number( + &animation_name_token, + &freespecial_recognized, + &freespecial_number, + alert_buffer, + sizeof(alert_buffer) + ); + + if(freespecial_error) { + shutdownmessage = freespecial_error; + goto lCleanup; + } + + /* + * "none" intentionally translates to FRAME_NONE and + * continues to disable the default synchronization. + */ + newanim->sync = freespecial_recognized + ? animspecials[freespecial_number - 1] + : translate_ani_id(value, NULL, NULL); + } + break; + case CMD_MODEL_DELAY: + { + const char* delay_error = command_token_get_delay( + GET_ARG(1), + GET_ARG(2), + &delay, + &delay_mode + ); + + if(delay_error) { + shutdownmessage = delay_error; + goto lCleanup; + } + + break; + } + case CMD_MODEL_OFFSET: + offset.x = GET_INT_ARG(1); + offset.y = GET_INT_ARG(2); + break; + case CMD_MODEL_SHADOWCOORDS: + shadow_xz[0] = GET_INT_ARG(1); + shadow_xz[1] = GET_INT_ARG(2); + shadow_set = 1; + break; + case CMD_MODEL_ENERGY_COST: + case CMD_MODEL_MPCOST: + + newanim->energy_cost.cost = GET_INT_ARG(1); + newanim->energy_cost.mponly = GET_INT_ARG(2); + newanim->energy_cost.disable = GET_INT_ARG(3); + + // Caskey, Damon V. + // 2019-11-22 + // + // Convert the idiotic arbitrary numeric value in text into bitwise + // logic with entity types. I was a buffoon when I first coded this + // feature and should have used bitwise logic to begin with. + switch (newanim->energy_cost.disable) + { + case 1: + newanim->energy_cost.disable |= TYPE_ENEMY; + newanim->energy_cost.disable |= TYPE_NPC; + newanim->energy_cost.disable |= TYPE_PLAYER; + break; + case 2: + newanim->energy_cost.disable |= TYPE_ENEMY; + newanim->energy_cost.disable |= TYPE_NPC; + break; + case 3: + newanim->energy_cost.disable |= TYPE_NPC; + newanim->energy_cost.disable |= TYPE_PLAYER; + break; + case 4: + newanim->energy_cost.disable |= TYPE_ENEMY; + newanim->energy_cost.disable |= TYPE_PLAYER; + break; + } + + break; + case CMD_MODEL_MPONLY: + newanim->energy_cost.mponly = GET_INT_ARG(1); + break; + case CMD_MODEL_CHARGETIME: + newanim->charge_time = GET_INT_ARG(1); + break; + + /* 2022-05-27 + * Caskey, Damon V. + * + * Index needs to come before any other child spawn + * read in command so we know which object index + * we want the child spawn commands to affect. + * + * The next in order are presets, so the creator + * can use a preset, then customize individual + * settings downstream. + */ + case CMD_MODEL_CHILD_SPAWN_INDEX: + temp_child_spawn_index = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_PRESET_BOMB: + + /* + * Shortcut for creator to set up a bomb projectile. + */ + + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->config = (CHILD_SPAWN_CONFIG_AUTOKILL_HIT | CHILD_SPAWN_CONFIG_BEHAVIOR_BOMB | CHILD_SPAWN_CONFIG_FACTION_DAMAGE_PARENT | CHILD_SPAWN_CONFIG_FACTION_HOSTILE_PARENT | CHILD_SPAWN_CONFIG_FACTION_INDIRECT_PARENT | CHILD_SPAWN_CONFIG_LAUNCH_TOSS | CHILD_SPAWN_CONFIG_MOVE_CONFIG_PARAMETER | CHILD_SPAWN_CONFIG_RELATIONSHIP_OWNER); + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->aimove = AIMOVE1_BOMB; + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->direction_adjust = DIRECTION_ADJUST_SAME; + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->move_config_flags = (MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL | MOVE_CONFIG_SUBJECT_TO_MAX_Z | MOVE_CONFIG_SUBJECT_TO_MIN_Z); + + break; + + case CMD_MODEL_CHILD_SPAWN_PRESET_SHOT: + + /* + * Shortcut for creator to set up a shot projectile. + */ + + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->config = (CHILD_SPAWN_CONFIG_AUTOKILL_HIT | CHILD_SPAWN_CONFIG_BEHAVIOR_SHOT | CHILD_SPAWN_CONFIG_FACTION_DAMAGE_PARENT | CHILD_SPAWN_CONFIG_FACTION_HOSTILE_PARENT | CHILD_SPAWN_CONFIG_FACTION_INDIRECT_PARENT | CHILD_SPAWN_CONFIG_GRAVITY_OFF | CHILD_SPAWN_CONFIG_LAUNCH_THROW | CHILD_SPAWN_CONFIG_MOVE_CONFIG_PARAMETER | CHILD_SPAWN_CONFIG_RELATIONSHIP_OWNER); + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->aimove = AIMOVE1_ARROW; + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->direction_adjust = DIRECTION_ADJUST_SAME; + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->move_config_flags = (MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL | MOVE_CONFIG_SUBJECT_TO_MAX_Z | MOVE_CONFIG_SUBJECT_TO_MIN_Z); + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->takedamage = arrow_takedamage; + + break; + + case CMD_MODEL_CHILD_SPAWN_AIMOVE: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->aimove = get_aimove_from_command_line(buf + pos, AIMOVE1_NONE); + break; + case CMD_MODEL_CHILD_SPAWN_CANDAMAGE: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->candamage = get_type_from_command_line(buf + pos); + break; + case CMD_MODEL_CHILD_SPAWN_COLOR: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->color = child_spawn_get_color_from_argument(filename, command, GET_ARG(1)); + break; + case CMD_MODEL_CHILD_SPAWN_CONFIG: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->config = child_spawn_get_config_argument(buf + pos, 0); + break; + case CMD_MODEL_CHILD_SPAWN_DIRECTION_ADJUST: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->direction_adjust = direction_get_adjustment_from_argument(filename, command, GET_ARG(1)); + break; + case CMD_MODEL_CHILD_SPAWN_HOSTILE: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->hostile = get_type_from_command_line(buf + pos); + break; + case CMD_MODEL_CHILD_SPAWN_MODEL: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->model_index = get_cached_model_index(GET_ARG(1)); + break; + case CMD_MODEL_CHILD_SPAWN_MOVE_CONSTRAINT: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->move_config_flags = get_move_config_flags_from_command_line(buf + pos); + break; + case CMD_MODEL_CHILD_SPAWN_OFFSET_X: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->position.x = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_OFFSET_Y: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->position.y = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_OFFSET_Z: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->position.z = GET_INT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_PROJECTILEHIT: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->projectilehit = get_type_from_command_line(buf + pos); + break; + case CMD_MODEL_CHILD_SPAWN_TAKEDAMAGE: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->takedamage = takedamage_get_reference_from_argument(GET_ARG(1)); + break; + case CMD_MODEL_CHILD_SPAWN_VELOCITY_X: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->velocity.x = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_VELOCITY_Y: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->velocity.y = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_CHILD_SPAWN_VELOCITY_Z: + child_spawn_upsert_property(&temp_child_spawn_head, temp_child_spawn_index)->velocity.z = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_COLLISIONONE: + newanim->attack_one = GET_INT_ARG(1); + break; + + /* 2020-03-02 + * Caskey, Damon V. + * + * This needs to come before any other collision + * parse command so we know which collision index + * we want the collision commands to affect. + */ + case CMD_MODEL_COLLISION_INDEX: + tempInt = GET_INT_ARG(1); + + if(!collision_validate_slot_index(tempInt)) { + snprintf(alert_buffer, sizeof(alert_buffer), + "Collision index (%d) out of range (0 to %d).", tempInt, MAX_COLLISION_BOXES_PER_FRAME - 1); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + temp_collision_index = tempInt; + break; + case CMD_MODEL_COUNTERATTACK: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->counterattack = GET_INT_ARG(1); + + break; + case CMD_MODEL_THROWFRAME: + case CMD_MODEL_PSHOTFRAME: + case CMD_MODEL_PSHOTFRAMEW: + case CMD_MODEL_PSHOTFRAMENO: + + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->throwframe = GET_FRAME_ARG(1); + + if (GET_INT_ARG(2)) + { + newanim->projectile->position.y = GET_INT_ARG(2); + } + break; + + + case CMD_MODEL_SHOOTFRAME: + + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->shootframe = GET_FRAME_ARG(1); + + if (GET_INT_ARG(2)) + { + newanim->projectile->position.y = GET_INT_ARG(2); + } + break; + + case CMD_MODEL_TOSSFRAME: + case CMD_MODEL_PBOMBFRAME: + + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->tossframe = GET_FRAME_ARG(1); + + if (GET_INT_ARG(2)) + { + newanim->projectile->position.y = GET_INT_ARG(2); + } + + // For legacy compatability. See bomb_spawn() for details. + newanim->projectile->velocity.y = MODEL_SPEED_NONE; + break; + case CMD_MODEL_CUSTKNIFE: + case CMD_MODEL_CUSTPSHOT: + case CMD_MODEL_CUSTPSHOTW: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->knife = get_cached_model_index(GET_ARG(1)); + break; + case CMD_MODEL_CUSTPSHOTNO: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->flash = get_cached_model_index(GET_ARG(1)); + break; + case CMD_MODEL_CUSTBOMB: + case CMD_MODEL_CUSTPBOMB: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->bomb = get_cached_model_index(GET_ARG(1)); + + break; + case CMD_MODEL_CUSTSTAR: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->star = get_cached_model_index(GET_ARG(1)); + break; + case CMD_MODEL_PROJECTILE_COLOR_SET_ADJUST: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + tempInt = COLORSET_ADJUST_NONE; + } + else if (stricmp(value, "parent_index") == 0) + { + tempInt = COLORSET_ADJUST_PARENT_INDEX; + } + else if (stricmp(value, "parent_table") == 0) + { + tempInt = COLORSET_ADJUST_PARENT_TABLE; + } + else + { + tempInt = GET_INT_ARG(1); + } + + newanim->projectile->color_set_adjust = tempInt; + break; + case CMD_MODEL_PROJECTILE_DIRECTION_ADJUST: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + tempInt = direction_get_adjustment_from_argument(filename, command, GET_ARG(1)); + + newanim->projectile->direction_adjust = tempInt; + break; + case CMD_MODEL_PROJECTILE_OFFENSE: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + value = GET_ARG(1); + + if (stricmp(value, "parent") == 0) + { + tempInt = PROJECTILE_OFFENSE_PARENT; + } + else if (stricmp(value, "self") == 0) + { + tempInt = PROJECTILE_OFFENSE_SELF; + } + + newanim->projectile->offense = tempInt; + break; + case CMD_MODEL_PROJECTILE_POSITION_X: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->position.x = GET_INT_ARG(1); + break; + case CMD_MODEL_PROJECTILE_POSITION_Y: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->position.y = GET_INT_ARG(1); + break; + case CMD_MODEL_PROJECTILE_POSITION_Z: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->position.z = GET_INT_ARG(1); + break; + case CMD_MODEL_PROJECTILE_VELOCITY_X: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->velocity.x = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_PROJECTILE_VELOCITY_Y: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->velocity.y = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_PROJECTILE_VELOCITY_Z: + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->velocity.z = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_STAR_VELOCITY: + + // If we don't have a projectile allcated, do it now. + if (!newanim->projectile) + { + newanim->projectile = allocate_projectile(); + } + + newanim->projectile->star_velocity[0] = GET_FLOAT_ARG(1); + newanim->projectile->star_velocity[1] = GET_FLOAT_ARG(2); + newanim->projectile->star_velocity[2] = GET_FLOAT_ARG(3); + break; + + // Legacy dive attacks. Turn off animation level subject_to_gravity + // and then use jumpframe to fly down at an angle. + case CMD_MODEL_DIVE: + newanim->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + + newanim->jumpframe = (s_onframe_move){ + .frame = 0, + .model_index = MODEL_INDEX_NONE, + .velocity = {.x = GET_FLOAT_ARG(1), .y = -GET_FLOAT_ARG(2), .z = 0 } + }; + break; + + case CMD_MODEL_DIVE1: + newanim->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + + newanim->jumpframe = (s_onframe_move){ + .frame = 0, + .model_index = MODEL_INDEX_NONE, + .velocity = {.x = GET_FLOAT_ARG(1), .y = 0, .z = 0 } + }; + break; + case CMD_MODEL_DIVE2: + newanim->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + + newanim->jumpframe = (s_onframe_move){ + .frame = 0, + .model_index = MODEL_INDEX_NONE, + .velocity = {.x = 0, .y = -GET_FLOAT_ARG(2), .z = 0 } + }; + break; + case CMD_MODEL_JUMPFRAME: + { + memset(&newanim->jumpframe.velocity, 0, sizeof(s_axis_principal_float)); + + newanim->jumpframe.frame = GET_FRAME_ARG(1); + newanim->jumpframe.velocity.y = GET_FLOAT_ARG(2); + + value = GET_ARG(3); + if(value[0]) + { + newanim->jumpframe.velocity.x = GET_FLOAT_ARG(3); + newanim->jumpframe.velocity.z = GET_FLOAT_ARG(4); + } + else // k, only for backward compatibility :(((((((((((((((( + { + if(newanim->jumpframe.velocity.y <= 0) + { + if(newchar->type == TYPE_PLAYER) + { + newanim->jumpframe.velocity.y = newchar->jumpheight / 2; + newanim->jumpframe.velocity.z = 0; + newanim->jumpframe.velocity.x = 2; + } + else + { + newanim->jumpframe.velocity.y = newchar->jumpheight; + newanim->jumpframe.velocity.z = newanim->jumpframe.velocity.x = 0; + } + } + else + { + if(newchar->type != TYPE_ENEMY && newchar->type != TYPE_NPC) + { + newanim->jumpframe.velocity.z = newanim->jumpframe.velocity.x = 0; + } + else + { + newanim->jumpframe.velocity.z = 0; + newanim->jumpframe.velocity.x = (float)1.3; + } + } + } + + value = GET_ARG(5); + if(value[0]) + { + newanim->jumpframe.model_index = get_cached_model_index(value); + } + else + { + newanim->jumpframe.model_index = MODEL_INDEX_NONE; + } + } + break; + case CMD_MODEL_BOUNCEFACTOR: + newanim->bounce_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_LANDFRAME: + // Landing frame. + newanim->landframe.frame = GET_FRAME_ARG(1); + + // Entity to spawn when land frame triggers. + value = GET_ARG(2); + if(value[0]) + { + newanim->landframe.model_index = get_cached_model_index(value); + } + + break; + case CMD_MODEL_DROPFRAME: + + newanim->dropframe.frame = GET_FRAME_ARG(1); + + // Entity to spawn when drop frame triggers. + value = GET_ARG(2); + if (value[0]) + { + newanim->dropframe.model_index = get_cached_model_index(value); + } + + break; + case CMD_MODEL_CANCEL: + { + s_command_token_reader token_reader = { + .cursor = buf + pos + }; + + s_command_token token; + + /* + * Parse into a temporary command first. This prevents + * a malformed line from partially modifying the model. + */ + s_com parsed_special = {0}; + + const char* parse_error; + + int64_t frame_min; + int64_t frame_max; + int64_t required_hits; + int freespecial_number; + + /* + * Cancel belongs to the currently active animation. + */ + if(!newanim || ani_id < 0) { + shutdownmessage = + "Cannot add cancel command: animation not specified.\n"; + + goto lCleanup; + } + + /* + * Consume and verify the command-name token. + */ + if(!command_token_reader_next(&token_reader, &token) || !command_token_equals(&token, "cancel")) { + shutdownmessage = "Invalid cancel command name.\n"; + goto lCleanup; + } + + /* + * Fixed cancel parameters precede the input sequence. + */ + if(!command_token_reader_next_int64(&token_reader, &frame_min)) { + shutdownmessage = + "Cancel command requires an integer start frame.\n"; + + goto lCleanup; + } + + if(!command_token_reader_next_int64(&token_reader, &frame_max)) { + shutdownmessage = + "Cancel command requires an integer end frame.\n"; + + goto lCleanup; + } + + if(!command_token_reader_next_int64(&token_reader, &required_hits)) { + shutdownmessage = + "Cancel command requires an integer hit count. " + "0 means no hit requirement.\n"; + + goto lCleanup; + } + + if(required_hits < 0) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Cancel command hit count (%" PRId64 ") cannot be negative.\n", + required_hits + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + /* + * Parse the variable-length input sequence. The + * freespecial token terminates the sequence. + */ + parse_error = special_command_parse_sequence( + &token_reader, + &parsed_special, + &freespecial_number, + alert_buffer, + sizeof(alert_buffer) + ); + + if(parse_error) { + shutdownmessage = parse_error; + goto lCleanup; + } + + /* + * Complete cancel-specific command properties. + */ + parsed_special.anim = animspecials[freespecial_number - 1]; + + parsed_special.frame.min = frame_min; + parsed_special.frame.max = frame_max; + parsed_special.cancel = ani_id; + parsed_special.hits = (uint64_t)required_hits; + + /* + * Commit only after the complete command has parsed + * and validated successfully. + */ + if(!alloc_specials(newchar)) { + shutdownmessage = E_OUT_OF_MEMORY; + goto lCleanup; + } + + newchar->special[newchar->specials_loaded] = parsed_special; + newanim->cancel = ANIMATION_CANCEL_ENABLED; + + newchar->specials_loaded++; + } + break; + + case CMD_MODEL_SOUND_INDEX: + tempInt = GET_INT_ARG(1); + + if (!frame_sound_validate_slot_index(tempInt)) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound index (%d) out of range (0 to %d).", + tempInt, + MAX_FRAME_SOUNDS_PER_FRAME - 1 + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + temp_frame_sound_index = tempInt; + break; + case CMD_MODEL_SOUND_CHANNEL_OFFSET: + { + s_command_token offset_token; + s_frame_sound_action* action; + uint64_t channel_offset; + int channel; + + if (!frame_sound_parse_channel(GET_ARG(1), &channel)) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound channel '%s' invalid. Expected an integer from 0 to %u.", + GET_ARG(1), + SOUND_CHANNEL_COUNT_MAX - 1U + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + offset_token.text = GET_ARG(2); + offset_token.length = strlen(offset_token.text); + + if (!command_token_get_uint64( + &offset_token, + &channel_offset + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound channel offset '%s'. Expected an unsigned PCM frame.", + offset_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + action = frame_sound_action_append( + &temp_frame_sound, + FRAME_SOUND_CHANNEL_ACTION_OFFSET + ); + action->channel = channel; + action->offset = channel_offset; + break; + } + case CMD_MODEL_SOUND_CHANNEL_PAUSE: + case CMD_MODEL_SOUND_CHANNEL_RESUME: + case CMD_MODEL_SOUND_CHANNEL_STOP: + { + s_frame_sound_action* action; + e_frame_sound_action action_type; + int channel; + + if (!frame_sound_parse_channel(GET_ARG(1), &channel)) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound channel '%s' invalid. Expected an integer from 0 to %u.", + GET_ARG(1), + SOUND_CHANNEL_COUNT_MAX - 1U + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + action_type = cmd == CMD_MODEL_SOUND_CHANNEL_PAUSE + ? FRAME_SOUND_CHANNEL_ACTION_PAUSE + : cmd == CMD_MODEL_SOUND_CHANNEL_RESUME + ? FRAME_SOUND_CHANNEL_ACTION_RESUME + : FRAME_SOUND_CHANNEL_ACTION_STOP; + + action = frame_sound_action_append( + &temp_frame_sound, + action_type + ); + action->channel = channel; + break; + } + case CMD_MODEL_SOUND_CHANNEL_PRIORITY: + { + s_command_token priority_token; + uint64_t priority; + + priority_token.text = GET_ARG(1); + priority_token.length = strlen(priority_token.text); + + if (!command_token_get_uint64( + &priority_token, + &priority + ) || priority > UINT_MAX) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound channel priority '%s'. Expected an unsigned integer from 0 to %u.", + priority_token.text, + UINT_MAX + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->priority = (unsigned int)priority; + break; + } + case CMD_MODEL_SOUND_CHANNEL_SET: + { + int channel; + + value = GET_ARG(1); + + if (stricmp(value, "auto") == 0) { + channel = -1; + } else if (!frame_sound_parse_channel(value, &channel)) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound channel '%s' invalid. Expected 'auto' or an integer from 0 to %u.", + value, + SOUND_CHANNEL_COUNT_MAX - 1U + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->channel = channel; + break; + } + case CMD_MODEL_SOUND_CHANCE: + tempInt = GET_INT_ARG(1); + + if (tempInt < 0 || tempInt > 100) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound chance (%d) out of range (0 to 100).", + tempInt + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->chance = (unsigned)tempInt; + break; + case CMD_MODEL_SOUND_DELAY: + { + s_command_token delay_token; + uint64_t sound_delay; + + delay_token.text = GET_ARG(1); + delay_token.length = strlen(delay_token.text); + + if (!command_token_get_uint64(&delay_token, &sound_delay)) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound delay '%s'. Expected an unsigned logical tick count.", + delay_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->delay = sound_delay; + break; + } + case CMD_MODEL_SOUND_GROUP: + { + const char* invalid_group; + sound_group_mask_t sound_group; + + if (!sound_group_get_flags_from_arglist( + &arglist, + &sound_group, + &invalid_group + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound group '%s' invalid. Expected none, all, all0, all1, a-z, or a1-z1.", + invalid_group + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->group = sound_group; + break; + } + case CMD_MODEL_SOUND_GROUP_OFFSET: + { + s_command_token offset_token; + s_frame_sound_action* action; + const char* invalid_group; + sound_group_mask_t sound_group; + uint64_t group_offset; + size_t offset_argument; + + if (arglist.count < 3) { + shutdownmessage = + "Sound group offset requires one or more groups followed by an unsigned PCM frame."; + goto lCleanup; + } + + offset_argument = arglist.count - 1U; + offset_token.text = GET_ARG(offset_argument); + offset_token.length = strlen(offset_token.text); + + if (!command_token_get_uint64( + &offset_token, + &group_offset + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound group offset '%s'. Expected an unsigned PCM frame.", + offset_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + if (!sound_group_get_flags_from_arglist_range( + &arglist, + 1, + offset_argument, + &sound_group, + &invalid_group + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound group '%s' invalid. Expected none, all, all0, all1, a-z, or a1-z1.", + invalid_group + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + action = frame_sound_action_append( + &temp_frame_sound, + FRAME_SOUND_GROUP_ACTION_OFFSET + ); + action->group = sound_group; + action->offset = group_offset; + break; + } + case CMD_MODEL_SOUND_GROUP_PAUSE: + case CMD_MODEL_SOUND_GROUP_RESUME: + case CMD_MODEL_SOUND_GROUP_STOP: + { + s_frame_sound_action* action; + e_frame_sound_action action_type; + const char* invalid_group; + sound_group_mask_t sound_group; + + if (!sound_group_get_flags_from_arglist( + &arglist, + &sound_group, + &invalid_group + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound group '%s' invalid. Expected none, all, all0, all1, a-z, or a1-z1.", + invalid_group + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + action_type = cmd == CMD_MODEL_SOUND_GROUP_PAUSE + ? FRAME_SOUND_GROUP_ACTION_PAUSE + : cmd == CMD_MODEL_SOUND_GROUP_RESUME + ? FRAME_SOUND_GROUP_ACTION_RESUME + : FRAME_SOUND_GROUP_ACTION_STOP; + + action = frame_sound_action_append( + &temp_frame_sound, + action_type + ); + action->group = sound_group; + break; + } + case CMD_MODEL_SOUND_LOADING: + { + bool stream; + + value = GET_ARG(1); + + if (stricmp(value, "cache") == 0) { + stream = false; + } else if (stricmp(value, "stream") == 0) { + stream = true; + } else { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound loading mode '%s' invalid. Expected 'cache' or 'stream'.", + value + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->stream = stream; + break; + } + case CMD_MODEL_SOUND_LOOP: + { + s_command_token loop_token; + uint64_t loop; + + loop_token.text = GET_ARG(1); + loop_token.length = strlen(loop_token.text); + + if (!command_token_get_uint64(&loop_token, &loop) + || loop > 1U) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound loop '%s'. Expected 0 or 1.", + loop_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->loop = loop != 0; + break; + } + case CMD_MODEL_SOUND_LOOP_OFFSET: + { + s_command_token loop_offset_token; + uint64_t loop_offset; + + loop_offset_token.text = GET_ARG(1); + loop_offset_token.length = strlen(loop_offset_token.text); + + if (!command_token_get_uint64( + &loop_offset_token, + &loop_offset + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound loop offset '%s'. Expected an unsigned PCM frame.", + loop_offset_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + )->loop_offset = loop_offset; + break; + } + case CMD_MODEL_SOUND_RANDOM: + { + const int random_min = GET_INT_ARG(1); + const int random_max = GET_INT_ARG(2); + + if (!frame_sound_validate_slot_index(random_min) + || !frame_sound_validate_slot_index(random_max) + || random_min > random_max) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Sound random range (%d to %d) invalid. Expected an ascending range from 0 to %d.", + random_min, + random_max, + MAX_FRAME_SOUNDS_PER_FRAME - 1 + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + if (!temp_frame_sound) { + temp_frame_sound = frame_sound_collection_allocate(); + } + + temp_frame_sound->random_status = + frame_sound_get_slot_range_mask(random_min, random_max); + break; + } + case CMD_MODEL_SOUND_START_OFFSET: + { + s_command_token start_offset_token; + s_frame_sound* frame_sound; + uint64_t start_offset; + + start_offset_token.text = GET_ARG(1); + start_offset_token.length = strlen(start_offset_token.text); + + if (stricmp(start_offset_token.text, "auto") == 0) { + frame_sound = frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + ); + frame_sound->start_offset = 0; + frame_sound->start_offset_supplied = false; + break; + } + + if (!command_token_get_uint64( + &start_offset_token, + &start_offset + )) { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Invalid sound start offset '%s'. Expected 'auto' or an unsigned PCM frame.", + start_offset_token.text + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + frame_sound = frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + ); + frame_sound->start_offset = start_offset; + frame_sound->start_offset_supplied = true; + break; + } + case CMD_MODEL_SOUND: + { + s_frame_sound* frame_sound = frame_sound_upsert_index( + &temp_frame_sound, + temp_frame_sound_index + ); + + value = GET_ARG(1); + if (stricmp(value, "none") == 0) { + frame_sound_set_source(frame_sound, NULL); + } else { + frame_sound_set_source(frame_sound, value); + } + + temp_frame_sound->active_status |= + frame_sound_get_slot_mask(temp_frame_sound_index); + break; + } + case CMD_MODEL_HITFX: + + value = GET_ARG(1); + + if(stricmp(value, "none") == 0) + { + tempInt = SAMPLE_ID_NONE; + } + else + { + tempInt = sound_load_sample(value, packfile, 1, 0); + } + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->hitsound = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_HITFLASH: + + /* First find out which flash model index (if any). */ + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + tempInt = MODEL_INDEX_NONE; + } + else + { + tempInt = get_cached_model_index(value); + } + + /* Apply model index to frame attack box or smartbomb? */ + + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->flash.model_hit = tempInt; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_hit = tempInt; + } + + break; + + case CMD_MODEL_BLOCKFLASH: + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_block = MODEL_INDEX_NONE; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_block = get_cached_model_index(value); + } + + break; + + case CMD_MODEL_BLOCKFX: + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blocksound = SAMPLE_ID_NONE; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blocksound = sound_load_sample(value, packfile, 1, 0); + } + + break; + + case CMD_MODEL_FASTATTACK: + + if(GET_INT_ARG(1)) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->next_hit_time = global_config.game_speed / 20; + } + + break; + + case CMD_MODEL_IGNOREATTACKID: + + if(GET_INT_ARG(1)) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->ignore_attack_id = 1; + } + + break; + + case CMD_MODEL_BBOX: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + + value = GET_ARG(1); + if (stricmp(value, "none") == 0) { + + *temp_collision_coordinates = (s_hitbox){ 0 }; + break; + } + + temp_collision_coordinates->x = GET_INT_ARG(1); + temp_collision_coordinates->y = GET_INT_ARG(2); + temp_collision_coordinates->width = GET_INT_ARG(3); + temp_collision_coordinates->height = GET_INT_ARG(4); + + /* + * 2023-01-13: If only the first Z depth provided, + * use it for both directions. We verify a numeric + * instead of checking for empty because the creator + * might intentionally apply a 0 value to foreground + * depth and a simple empty check would override it. + */ + + tempInt = GET_INT_ARG(5); + + temp_collision_coordinates->z_background = tempInt; + + value = GET_ARG(6); + + if (isNumeric(value)) { + tempInt = GET_INT_ARG(6); + } + + temp_collision_coordinates->z_foreground = tempInt; + + break; + case CMD_MODEL_BBOX_COORDINATES: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + + temp_collision_coordinates->x = GET_INT_ARG(1); + temp_collision_coordinates->y = GET_INT_ARG(2); + temp_collision_coordinates->width = GET_INT_ARG(3); + temp_collision_coordinates->height = GET_INT_ARG(4); + temp_collision_coordinates->z_background = GET_INT_ARG(5); + temp_collision_coordinates->z_foreground = GET_INT_ARG(6); + + break; + case CMD_MODEL_BBOX_INDEX: + // Does nothing. Do not modify. + break; + case CMD_MODEL_BBOX_EFFECT_HIT_FLASH_LAYER_ADJUST: + collision_body_upsert_property(&temp_collision_body, temp_collision_index)->flash.layer_adjust = GET_INT_ARG(1); + break; + case CMD_MODEL_BBOX_EFFECT_HIT_FLASH_LAYER_SOURCE: + collision_body_upsert_property(&temp_collision_body, temp_collision_index)->flash.layer_source = GET_INT_ARG(1); + break; + case CMD_MODEL_BBOX_EFFECT_HIT_FLASH_Z_SOURCE: + collision_body_upsert_property(&temp_collision_body, temp_collision_index)->flash.z_source = GET_INT_ARG(1); + break; + case CMD_MODEL_BBOX_POSITION_X: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->x = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOX_POSITION_Y: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->y = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOX_SIZE_X: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->width = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOX_SIZE_Y: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->height = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOX_SIZE_Z_1: + case CMD_MODEL_BBOX_SIZE_Z_BACKGROUND: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->z_background = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOX_SIZE_Z_2: + case CMD_MODEL_BBOX_SIZE_Z_FOREGROUND: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->z_foreground = GET_INT_ARG(1); + + break; + case CMD_MODEL_BBOXZ: + + collision_body_upsert_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates = collision_body_upsert_coordinates_property(&temp_collision_body, temp_collision_index); + temp_collision_coordinates->z_background = GET_INT_ARG(1); + temp_collision_coordinates->z_foreground = GET_INT_ARG(2); + + break; + case CMD_MODEL_PLATFORM: + newchar->hasPlatforms = 1; + for(i = 0; i < arglist.count && arglist.args[i] && arglist.args[i][0]; i++); + if(i < 8) + { + for(i = 0; i < 6; i++) + { + platform[i + 2] = GET_FLOAT_ARG(i + 1); + } + platform[PLATFORM_X] = PLATFORM_DEFAULT_X; + } + else for(i = 0; i < 8; i++) + { + platform[i] = GET_FLOAT_ARG(i + 1); + } + break; + case CMD_MODEL_DRAWMETHOD: + value = GET_ARG(1); + if(isNumeric(value)) + { + // special effects + drawmethod.scalex = GET_INT_ARG(1); + drawmethod.scaley = GET_INT_ARG(2); + drawmethod.config = GET_INT_ARG(3) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_X) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_X); + drawmethod.config = GET_INT_ARG(4) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_Y) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_Y); + drawmethod.shiftx = GET_INT_ARG(5); + drawmethod.alpha = GET_INT_ARG(6); + drawmethod.remap = GET_INT_ARG(7); + drawmethod.fillcolor = parsecolor(GET_ARG(8)); + drawmethod.rotate = GET_INT_ARG(9); + drawmethod.config = GET_INT_ARG(10) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_ROTATE) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_ROTATE); + } + else if (0 == stricmp(value, "scale")) + { + drawmethod.scalex = GET_FLOAT_ARG(2) * 256; + drawmethod.scaley = arglist.count > 3 ? GET_FLOAT_ARG(3) * 256 : drawmethod.scalex; + } + else if (0 == stricmp(value, "scalex")) + { + drawmethod.scalex = GET_FLOAT_ARG(2) * 256; + } + else if (0 == stricmp(value, "scaley")) + { + drawmethod.scaley = GET_FLOAT_ARG(2) * 256; + } + else if (0 == stricmp(value, "xrepeat")) + { + drawmethod.xrepeat = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "yrepeat")) + { + drawmethod.yrepeat = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "xspan")) + { + drawmethod.xspan = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "yspan")) + { + drawmethod.yspan = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "flipx")) + { + drawmethod.config = GET_INT_ARG(2) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_X) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_X); + } + else if (0 == stricmp(value, "flipy")) + { + drawmethod.config = GET_INT_ARG(2) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_Y) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_Y); + } + else if (0 == stricmp(value, "shiftx")) + { + drawmethod.shiftx = GET_FLOAT_ARG(2) * 256; + } + else if (0 == stricmp(value, "rotate")) + { + drawmethod.rotate = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "fliprotate")) + { + drawmethod.config = GET_INT_ARG(2) ? (drawmethod.config | DRAWMETHOD_CONFIG_FLIP_ROTATE) : (drawmethod.config & ~DRAWMETHOD_CONFIG_FLIP_ROTATE); + } + else if (0 == stricmp(value, "fillcolor")) + { + drawmethod.fillcolor = parsecolor(GET_ARG(2)); + } + else if (0 == stricmp(value, "remap")) + { + drawmethod.remap = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "channel")) + { + drawmethod.channelr = GET_FLOAT_ARG(2) * 255; + drawmethod.channelg = arglist.count > 3 ? GET_FLOAT_ARG(3) * 255 : drawmethod.channelr; + drawmethod.channelb = arglist.count > 4 ? GET_FLOAT_ARG(4) * 255 : drawmethod.channelr; + } + else if (0 == stricmp(value, "channelr")) + { + drawmethod.channelr = GET_FLOAT_ARG(2) * 255; + } + else if (0 == stricmp(value, "channelg")) + { + drawmethod.channelg = GET_FLOAT_ARG(2) * 255; + } + else if (0 == stricmp(value, "channelb")) + { + drawmethod.channelb = GET_FLOAT_ARG(2) * 255; + } + else if (0 == stricmp(value, "tintmode")) + { + drawmethod.tintmode = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "tintcolor")) + { + drawmethod.tintcolor = parsecolor(GET_ARG(2)); + } + else if (0 == stricmp(value, "alpha")) + { + drawmethod.alpha = GET_INT_ARG(2); + } + else if (0 == stricmp(value, "clip")) + { + drawmethod.clipx = GET_INT_ARG(2); + drawmethod.clipy = GET_INT_ARG(3); + drawmethod.clipw = GET_INT_ARG(4); + drawmethod.cliph = GET_INT_ARG(5); + } + if(drawmethod.scalex < 0) + { + drawmethod.scalex = -drawmethod.scalex; + drawmethod.config ^= DRAWMETHOD_CONFIG_FLIP_X; + } + if(drawmethod.scaley < 0) + { + drawmethod.scaley = -drawmethod.scaley; + drawmethod.config ^= DRAWMETHOD_CONFIG_FLIP_Y; + } + if(drawmethod.rotate) + { + drawmethod.rotate = ((int)drawmethod.rotate % 360 + 360) % 360; + } + if(!blendfx_is_set) + { + if(drawmethod.alpha > 0 && drawmethod.alpha <= MAX_BLENDINGS) + { + blendfx[drawmethod.alpha - 1] = 1; + } + } + drawmethod.config |= DRAWMETHOD_CONFIG_ENABLED; + break; + case CMD_MODEL_NODRAWMETHOD: + //disable special effects + drawmethod.config &= ~DRAWMETHOD_CONFIG_ENABLED; + break; + + // 2016-10-11 + // Caskey, Damon + // Broken down attack commands. + case CMD_MODEL_COLLISION_BLOCK_COST: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->guardcost = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_BLOCK_PENETRATE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_block = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_COORDINATES: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + + temp_collision_coordinates->x = GET_INT_ARG(1); + temp_collision_coordinates->y = GET_INT_ARG(2); + temp_collision_coordinates->width = GET_INT_ARG(3); + temp_collision_coordinates->height = GET_INT_ARG(4); + temp_collision_coordinates->z_background = GET_INT_ARG(5); + temp_collision_coordinates->z_foreground = GET_INT_ARG(6); + + break; + + case CMD_MODEL_COLLISION_COUNTER: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->counterattack = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_FORCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_force = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_LAND_FORCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->damage_on_landing.attack_force = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_LAND_MODE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blast = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_LETHAL_DISABLE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_kill = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_STEAL: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->steal = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_TYPE: + + value = GET_ARG(1); + + tempInt = get_attack_type_from_string(value, filename); + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_FORCE: + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->force = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_INDEX: + + tempInt = GET_INT_ARG(1); + + if(tempInt < 0 || tempInt >= MAX_RECURSIVE_EFFECTS) { + + snprintf(alert_buffer, sizeof(alert_buffer), "Recursive index (%d) out of range (0 - %d).", + tempInt, MAX_RECURSIVE_EFFECTS - 1); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->index = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_MODE: + + /* + * Caskey, Damon V. + * 2021-08-24 + * + * For legacy support of mode, we have to handle + * integer values differently because like a bonehead, + * I didn't originally set them up with bitwise + * logic in mind. + */ + + value = GET_ARG(1); + + if (isNumeric(value)) + { + /* + * Numeric is legacy. Interpret the number as + * a list of modes. + */ + + tempInt = GET_INT_ARG(1); + + tempInt = recursive_effect_get_mode_setup_from_legacy_argument(tempInt); + } + else + { + /* + * Toggle bits based on items provided in argument list. + */ + + tempInt = recursive_effect_get_mode_setup_from_command_line(buf + pos); + } + + + /* Send resulting bitwise integer to mode value. */ + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->mode = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TAG: + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->meta_tag = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TYPE: + + value = GET_ARG(1); + + tempInt = get_attack_type_from_string(value, filename); + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->type = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_RATE: + + tempInt = GET_INT_ARG(1); + + if (tempInt < 0) { + snprintf(alert_buffer, sizeof(alert_buffer), + "Recursive rate (%d) cannot be negative.", tempInt); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->rate = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_DAMAGE_RECURSIVE_TIME_EXPIRE: + + tempInt = GET_INT_ARG(1); + + if (tempInt < 0) { + snprintf(alert_buffer, sizeof(alert_buffer), + "Recursive time expire (%d) cannot be negative.", tempInt); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + collision_attack_upsert_recursive_property(&temp_collision_attack, temp_collision_index)->time = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_REACTION_FALL_FORCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_X: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.x = GET_FLOAT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Y: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.y = GET_FLOAT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_FALL_VELOCITY_Z: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.z = GET_FLOAT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_REPOSITION_DIRECTION: + + tempInt = direction_get_adjustment_from_argument(filename, command, GET_ARG(1)); + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->force_direction = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_EFFECT_BLOCK_FLASH: + + value = GET_ARG(1); + + if(stricmp(value, "none") == 0 || value == 0) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_block = MODEL_INDEX_NONE; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_block = get_cached_model_index(value); + } + + break; + + case CMD_MODEL_COLLISION_EFFECT_BLOCK_SOUND: + + value = GET_ARG(1); + + if(stricmp(value, "none") == 0) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blocksound = SAMPLE_ID_NONE; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blocksound = sound_load_sample(value, packfile, 1, 0); + } + + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH: + + value = GET_ARG(1); + + if(stricmp(value, "none") == 0 || value == 0) + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_hit = MODEL_INDEX_NONE; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.model_hit = get_cached_model_index(value); + } + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_DISABLE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_flash = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_LAYER_ADJUST: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.layer_adjust = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_LAYER_SOURCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.layer_source = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_FLASH_Z_SOURCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->flash.z_source = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_EFFECT_HIT_SOUND: + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + tempInt = SAMPLE_ID_NONE; + } + else + { + tempInt = sound_load_sample(value, packfile, 1, 0); + } + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->hitsound = tempInt; + + tempInt = 0; + + break; + + case CMD_MODEL_COLLISION_GROUND: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->otg = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_MAP_INDEX: + + /* + * Translate text value into a pre-defined forcemap constant. + * When applying a pre-defined forcemap, we'll look at the model + * and try to find its appropriate index. For example, if the + * pre-defined BURN is used, forcemap will apply the model's + * designated burn. This allows use of effect maps without the + * need to match all model palettes up (i.e. all having their + * second palette a burn palette). + * + * If the creator provides a numeric value, pass it straight + * through. + */ + + value = GET_ARG(1); + if (stricmp(value, "none") == 0) + { + tempInt = MAP_TYPE_NONE; + } + else if (stricmp(value, "burn") == 0) + { + tempInt = MAP_TYPE_BURN; + } + else if (stricmp(value, "freeze") == 0) + { + tempInt = MAP_TYPE_FREEZE; + } + else if (stricmp(value, "ko") == 0) + { + tempInt = MAP_TYPE_SHOCK; + } + else if (stricmp(value, "shock") == 0) + { + tempInt = MAP_TYPE_SHOCK; + } + else + { + tempInt = GET_INT_ARG(1); + } + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->forcemap = tempInt; + + break; + + case CMD_MODEL_COLLISION_MAP_TIME: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->maptime = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_POSITION_X: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->x = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_POSITION_Y: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->y = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_FREEZE_MODE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freeze = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_FREEZE_TIME: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freezetime = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_INVINCIBLE_TIME: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->next_hit_time = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_REPOSITION_DISTANCE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->grab_distance = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_REPOSITION_MODE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->grab = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_PAIN_SKIP: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_pain = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_REACTION_PAUSE_TIME: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->pause_add = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SEAL_COST: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->seal = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SEAL_TIME: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->sealtime = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SIZE_X: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->width = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SIZE_Y: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->height = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SIZE_Z_1: + case CMD_MODEL_COLLISION_SIZE_Z_BACKGROUND: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->z_background = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_SIZE_Z_2: + case CMD_MODEL_COLLISION_SIZE_Z_FOREGROUND: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->z_foreground = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_STAYDOWN_RISE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->staydown.rise = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_STAYDOWN_RISEATTACK: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->staydown.riseattack = GET_INT_ARG(1); + + break; + + case CMD_MODEL_COLLISION_TAG: + + // To do - Add tag system. + + break; + + case CMD_MODEL_COLLISION: + case CMD_MODEL_COLLISION1: + case CMD_MODEL_COLLISION2: + case CMD_MODEL_COLLISION3: + case CMD_MODEL_COLLISION4: + case CMD_MODEL_COLLISION5: + case CMD_MODEL_COLLISION6: + case CMD_MODEL_COLLISION7: + case CMD_MODEL_COLLISION8: + case CMD_MODEL_COLLISION9: + case CMD_MODEL_COLLISION10: + case CMD_MODEL_SHOCK: + case CMD_MODEL_BURN: + case CMD_MODEL_STEAL: + case CMD_MODEL_FREEZE: + case CMD_MODEL_ITEMBOX: + case CMD_MODEL_LOSE: + case CMD_MODEL_COLLISION_ETC: + + // 2020-03-08, + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + + /* + * "none" deactivates attack on this and subsequent frames. + * The attack loading system interprets 0 for all coordinates + * as no attack and will not load to memory. + */ + value = GET_ARG(1); + if (stricmp(value, "none") == 0) { + + *temp_collision_coordinates = (s_hitbox){0}; + + break; + } + + temp_collision_coordinates->x = GET_INT_ARG(1); + temp_collision_coordinates->y = GET_INT_ARG(2); + temp_collision_coordinates->width = GET_INT_ARG(3); + temp_collision_coordinates->height = GET_INT_ARG(4); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_force = GET_INT_ARG(5); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = GET_INT_ARG(6); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_block = GET_INT_ARG(7); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_flash = GET_INT_ARG(8); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->pause_add = GET_INT_ARG(9); + + // -- Not a typo - legacy Z sets identical value to back/fore depth. + temp_collision_coordinates->z_background = GET_INT_ARG(10); + temp_collision_coordinates->z_foreground = GET_INT_ARG(10); + + switch(cmd) + { + case CMD_MODEL_COLLISION: + case CMD_MODEL_COLLISION1: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL; + + break; + + case CMD_MODEL_COLLISION2: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL2; + + break; + + case CMD_MODEL_COLLISION3: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL3; + + break; + + case CMD_MODEL_COLLISION4: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL4; + + break; + + case CMD_MODEL_COLLISION5: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL5; + + break; + + case CMD_MODEL_COLLISION6: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL6; + + break; + + case CMD_MODEL_COLLISION7: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL7; + + break; + + case CMD_MODEL_COLLISION8: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL8; + + break; + + case CMD_MODEL_COLLISION9: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL9; + + break; + + case CMD_MODEL_COLLISION10: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_NORMAL10; + + break; + + case CMD_MODEL_SHOCK: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_SHOCK; + + break; + + case CMD_MODEL_BURN: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_BURN; + + break; + + case CMD_MODEL_STEAL: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->steal = 1; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_STEAL; + + break; + + case CMD_MODEL_FREEZE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_FREEZE; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freeze = 1; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freezetime = GET_FLOAT_ARG(6) * global_config.game_speed; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->forcemap = MAP_TYPE_FREEZE; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = 0; + + break; + + case CMD_MODEL_ITEMBOX: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_ITEM; + + break; + + case CMD_MODEL_LOSE: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_LOSE; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = 0; + + break; + + default: + + //value = GET_ARG(1); + + tempInt = get_attack_type_from_string(command, filename); + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = tempInt; + + break; + } + break; + + + + case CMD_MODEL_HITWALLTYPE: + value = GET_ARG(1); + newchar->hitwalltype = atoi(value); + break; + case CMD_MODEL_COLLISIONZ: + case CMD_MODEL_HITZ: + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->z_background = GET_INT_ARG(1); + temp_collision_coordinates->z_foreground = GET_INT_ARG(2); + + break; + + case CMD_MODEL_BLAST: + + // 2020-03-08, + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates = collision_attack_upsert_coordinates_property(&temp_collision_attack, temp_collision_index); + temp_collision_coordinates->x = GET_INT_ARG(1); + temp_collision_coordinates->y = GET_INT_ARG(2); + temp_collision_coordinates->width = GET_INT_ARG(3); + temp_collision_coordinates->height = GET_INT_ARG(4); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_force = GET_INT_ARG(5); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_block = GET_INT_ARG(6); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_flash = GET_INT_ARG(7); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->pause_add = GET_INT_ARG(8); + + // -- Not a typo - legacy Z sets identical value to back/fore depth. + temp_collision_coordinates->z_background = GET_INT_ARG(9); + temp_collision_coordinates->z_foreground = GET_INT_ARG(9); + + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = 1; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_type = ATK_BLAST; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blast = 1; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.x = default_model_dropv.x * 2.083f; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.y = default_model_dropv.y; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.z = default_model_dropv.z; + + break; + + case CMD_MODEL_DROPV: + + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->dropv.y = GET_FLOAT_ARG(1); + newchar->smartbomb->dropv.x = GET_FLOAT_ARG(2); + newchar->smartbomb->dropv.z = GET_FLOAT_ARG(3); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.y = GET_FLOAT_ARG(1); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.x = GET_FLOAT_ARG(2); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->dropv.z = GET_FLOAT_ARG(3); + } + + break; + + case CMD_MODEL_OTG: + // Over The Ground hit. + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->otg = GET_INT_ARG(1); + + break; + + case CMD_MODEL_JUGGLECOST: + + // if cost >= opponents jugglepoints , we can juggle + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->jugglecost = GET_INT_ARG(1); + + break; + + case CMD_MODEL_GUARDCOST: + + // if cost >= opponents guardpoints , opponent will play guardcrush anim + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->guardcost = GET_INT_ARG(1); + + break; + + case CMD_MODEL_STUN: + + //Like Freeze, but no auto remap. + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->freeze = 1; + newchar->smartbomb->freezetime = GET_FLOAT_ARG(1) * global_config.game_speed; + newchar->smartbomb->attack_drop = 0; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freeze = 1; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->freezetime = GET_FLOAT_ARG(1) * global_config.game_speed; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->attack_drop = 0; + } + + break; + + case CMD_MODEL_GRABIN: + + // fake grab distanse efffect, not link + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->grab = GET_INT_ARG(1); + newchar->smartbomb->grab_distance = GET_FLOAT_ARG(2); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->grab = GET_INT_ARG(1); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->grab_distance = GET_FLOAT_ARG(2); + } + + break; + + case CMD_MODEL_NOREFLECT: + + // only cost target's hp, don't knock down or cause pain, unless the target is killed + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->no_pain = GET_INT_ARG(1); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_pain = GET_INT_ARG(1); + } + + break; + + case CMD_MODEL_NOKILL: + + // don't kill the target, leave 1 hp + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->no_kill = GET_INT_ARG(1); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->no_kill = GET_INT_ARG(1); + } + + break; + + case CMD_MODEL_FORCEDIRECTION: + + tempInt = direction_get_adjustment_from_argument(filename, command, GET_ARG(1)); + + // the attack direction + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->force_direction = tempInt; + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->force_direction = tempInt; + } + + tempInt = 0; + + break; + + case CMD_MODEL_DAMAGEONLANDING: + + // fake throw damage on landing + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->damage_on_landing.attack_force = GET_INT_ARG(1); + newchar->smartbomb->blast = GET_INT_ARG(2); + newchar->smartbomb->damage_on_landing.attack_type = translate_attack_type(GET_ARG(3), filename); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->damage_on_landing.attack_force = GET_INT_ARG(1); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->blast = GET_INT_ARG(2); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->damage_on_landing.attack_type = translate_attack_type(GET_ARG(3), filename); + } + + break; + + case CMD_MODEL_SEAL: + + // Disable special moves for specified time. + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->sealtime = GET_INT_ARG(1) * (global_config.game_speed / 100); + newchar->smartbomb->seal = GET_INT_ARG(2); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->sealtime = GET_INT_ARG(1) * (global_config.game_speed / 100); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->seal = GET_INT_ARG(2); + } + + break; + + case CMD_MODEL_STAYDOWN: + + // Disable special moves for specified time. + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->staydown.rise = GET_INT_ARG(1); + newchar->smartbomb->staydown.riseattack = GET_INT_ARG(2); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->staydown.rise = GET_INT_ARG(1); + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->staydown.riseattack = GET_INT_ARG(2); + } + + break; + + case CMD_MODEL_FORCEMAP: + + /* + * Translate text value into a pre-defined forcemap constant. + * When applying a pre-defined forcemap, we'll look at the model + * and try to find its appropriate index. For example, if the + * pre-defined BURN is used, forcemap will apply the model's + * designated burn. This allows use of effect maps without the + * need to match all model palettes up (i.e. all having their + * second palette a burn palette). + * + * If the creator provides a numeric value, pass it straight + * through. + */ + + value = GET_ARG(1); + + if (stricmp(value, "none") == 0) + { + tempInt = MAP_TYPE_NONE; + } + else if (stricmp(value, "burn") == 0) + { + tempInt = MAP_TYPE_BURN; + } + else if (stricmp(value, "freeze") == 0) + { + tempInt = MAP_TYPE_FREEZE; + } + else if (stricmp(value, "ko") == 0) + { + tempInt = MAP_TYPE_SHOCK; + } + else if (stricmp(value, "shock") == 0) + { + tempInt = MAP_TYPE_SHOCK; + } + else + { + tempInt = GET_INT_ARG(1); + } + + // force color map change for specified time + if (!newanim && newchar->smartbomb) + { + newchar->smartbomb->forcemap = tempInt; + newchar->smartbomb->maptime = GET_FLOAT_ARG(2) * (global_config.game_speed / 100); + } + else + { + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->forcemap = tempInt; + collision_attack_upsert_property(&temp_collision_attack, temp_collision_index)->maptime = GET_FLOAT_ARG(2) * (global_config.game_speed / 100); + } + + break; + + case CMD_MODEL_IDLE: + idle = GET_INT_ARG(1); + break; + case CMD_MODEL_SETA: + move.base = GET_INT_ARG(1); + break; + case CMD_MODEL_MOVE: + move.axis.x = GET_INT_ARG(1); + break; + case CMD_MODEL_MOVEA: + move.axis.y = GET_INT_ARG(1); + break; + case CMD_MODEL_MOVEZ: + move.axis.z = GET_INT_ARG(1); + break; + case CMD_MODEL_FSHADOW: + + value = GET_ARG(1); + if (stricmp(value, "none") == 0) { + tempInt = FRAME_SHADOW_NONE; + + } else { + tempInt = GET_INT_ARG(1); + } + frameshadow = tempInt; + break; + case CMD_MODEL_RANGE: + if(!newanim) { + shutdownmessage = "Cannot set range: no animation!"; + goto lCleanup; + } + newanim->range.x.min = GET_INT_ARG(1); + newanim->range.x.max = GET_INT_ARG(2); + if(newanim->range.x.min == newanim->range.x.max) { + newanim->range.x.min--; + } + break; + case CMD_MODEL_RANGEZ: + if(!newanim) { + shutdownmessage = "Cannot set rangez: no animation!"; + goto lCleanup; + } + newanim->range.z.min = GET_INT_ARG(1); + newanim->range.z.max = GET_INT_ARG(2); + break; + case CMD_MODEL_RANGEA: + if(!newanim) { + shutdownmessage = "Cannot set rangea: no animation!"; + goto lCleanup; + } + newanim->range.y.min = GET_INT_ARG(1); + newanim->range.y.max = GET_INT_ARG(2); + break; + case CMD_MODEL_RANGEB: + if(!newanim) { + shutdownmessage = "Cannot set rangeb: no animation!"; + goto lCleanup; + } + newanim->range.base.min = GET_INT_ARG(1); + newanim->range.base.max = GET_INT_ARG(2); + break; + case CMD_MODEL_PATHFINDSTEP: + newchar->pathfindstep = GET_FLOAT_ARG(1); + break; + case CMD_MODEL_FRAME: + { + s_command_token frame_token; + s_command_token_reader frame_reader; + + // Command title for log. Details will be added blow accordingly. + //printf("\t\t\tFrame: "); + + if(!newanim) { + shutdownmessage = "Cannot add frame: animation not specified!"; + goto lCleanup; + } + + peek = 0; + if(frameset && framecount >= 0) { + framecount = -framecount; + } + + while(!frameset) { + frame_reader.cursor = buf + pos + peek; + + if(command_token_reader_next( + &frame_reader, + &frame_token + )) { + if(command_token_equals(&frame_token, "frame")) { + framecount++; + } + + if(command_token_equals(&frame_token, "anim")) { + frameset = 1; + } + } + + if(pos + peek >= size) { + frameset = 1; + } + + // Go to next line + while(buf[pos + peek] && buf[pos + peek] != '\n' && buf[pos + peek] != '\r') { + ++peek; + } + + while(buf[pos + peek] == '\n' || buf[pos + peek] == '\r') { + ++peek; + } + } + value = GET_ARG(1); + + // Log info. + //printf("%d", ++framenum); + //printf("\tSprite Path: %s\n", value); + + index = stricmp(value, "none") == 0 ? -1 : loadsprite(value, offset.x, offset.y, nopalette ? PIXEL_x8 : PIXEL_8); //don't use palette for the sprite since it will one palette from the entity's remap list in 24bit mode + + if(index >= 0) { + + // If the model does not have a designated palette + // yet and the creator did not specify palette none + // or global palette, then we will use this frame's + // sprite to load a color table. Effectively the first + // frame of a model becomes its palette base. + if(pixelformat == PIXEL_x8 && !nopalette) { + + // No master color table assigned yet? + if(newchar->palette == NULL) { + //printf("\t\t\tAuto Palette - 'Palette' not defined. Attempting to load color table from this frame: "); + + // Allocate memory for color table. + newchar->palette = malloc(PAL_BYTES); + // + if(loadimagepalette(value, packfile, newchar->palette) == 0) { + //printf("\t\t\t%s%s\n", "Failed to load color table from image: ", value); + goto lCleanup; + } + } + + //printf("\t\t\t%s\n", "Success. Loaded color selection 0 from frame."); + + // Assign the color table to sprite. + if(!nopalette) { + sprite_map[index].node->sprite->palette = newchar->palette; + sprite_map[index].node->sprite->pixelformat = pixelformat; + } + } + + if(maskindex >= 0) { + sprite_map[index].node->sprite->mask = sprite_map[maskindex].node->sprite; + maskindex = -1; + } + } + + if(platform[PLATFORM_X] == PLATFORM_DEFAULT_X) { // old style + platform_con[PLATFORM_X] = 0; + platform_con[PLATFORM_Z] = 3; + platform_con[PLATFORM_UPPERLEFT] = platform[PLATFORM_UPPERLEFT] - offset.x; + platform_con[PLATFORM_LOWERLEFT] = platform[PLATFORM_LOWERLEFT] - offset.x; + platform_con[PLATFORM_UPPERRIGHT] = platform[PLATFORM_UPPERRIGHT] - offset.x; + platform_con[PLATFORM_LOWERRIGHT] = platform[PLATFORM_LOWERRIGHT] - offset.x; + platform_con[PLATFORM_DEPTH] = platform[PLATFORM_DEPTH] + 3; + + } else { // wall style + platform_con[PLATFORM_X] = platform[PLATFORM_X] - offset.x; + platform_con[PLATFORM_Z] = platform[PLATFORM_Z] - offset.y; + platform_con[PLATFORM_UPPERLEFT] = platform[PLATFORM_UPPERLEFT]; + platform_con[PLATFORM_LOWERLEFT] = platform[PLATFORM_LOWERLEFT]; + platform_con[PLATFORM_UPPERRIGHT] = platform[PLATFORM_UPPERRIGHT]; + platform_con[PLATFORM_LOWERRIGHT] = platform[PLATFORM_LOWERRIGHT]; + platform_con[PLATFORM_DEPTH] = platform[PLATFORM_DEPTH]; + } + platform_con[PLATFORM_HEIGHT] = platform[PLATFORM_HEIGHT]; + + if(shadow_set) { + shadow_coords[0] = shadow_xz[0] - offset.x; + shadow_coords[1] = shadow_xz[1] - offset.y; + + } else { + shadow_coords[0] = shadow_coords[1] = 0; + } + + if(drawmethod.config & DRAWMETHOD_CONFIG_ENABLED) { + dm = drawmethod; + if(dm.clipw) { + dm.clipx -= offset.x; + dm.clipy -= offset.y; + } + + } else { + dm.config &= ~DRAWMETHOD_CONFIG_ENABLED; + } + + frame_sound_load_collection(temp_frame_sound, packfile); + + add_frame_data.animation = newanim; + add_frame_data.spriteindex = index; + add_frame_data.framecount = framecount; + add_frame_data.delay = delay; + add_frame_data.delay_mode = delay_mode; + add_frame_data.idle = idle; + add_frame_data.move = &move; + add_frame_data.platform = platform_con; + add_frame_data.frameshadow = frameshadow; + add_frame_data.shadow_coords = shadow_coords; + add_frame_data.sound = temp_frame_sound; + add_frame_data.drawmethod = &dm; + add_frame_data.offset = &offset; + + add_frame_data.model = newchar; + + /* + * Delete nodes from frame object lists + * that don't have valid data (i.e. no + * coordinates defined at all or the + * coordinates X/Y/H/W are all 0). + * + * 2023-01-09 - Disabled. Clone functions now + * verify coordinates before copying temp + * to frame. This is to preserve legacy behavior + * of keeping the read in values frame to frame + * even after a collision box is closed. + */ + //collision_attack_remove_undefined_coordinates(&temp_collision_attack); + //collision_body_remove_undefined_coordinates(&temp_collision_body); + + /* + * Multiple per frame object heads may have + * changed, so this needs to be right before + * addframe function. + */ + add_frame_data.collision_attack = temp_collision_attack; + add_frame_data.collision_body = temp_collision_body; + add_frame_data.collision_space = temp_collision_space; + add_frame_data.child_spawn = temp_child_spawn_head; + + /* + * Send data to frame function so it + * can build a new animation frame. + */ + curframe = addframe(&add_frame_data); + + /* + * Clear any data tha we don't want + * retained for the next frame. Also + * reset temp indexes. + */ + + temp_collision_index = 0; + + frame_sound_collection_free(temp_frame_sound); + temp_frame_sound = NULL; + temp_frame_sound_index = 0; + + child_spawn_free_list(temp_child_spawn_head); + temp_child_spawn_head = NULL; + temp_child_spawn_index = 0; + + frm_id = -1; + at_cmd_mergeable = false; + } + break; + case CMD_MODEL_ALPHAMASK: + if(!newanim) + { + shutdownmessage = "Cannot add alpha mask: animation not specified!"; + goto lCleanup; + } + if(maskindex >= 0) + { + shutdownmessage = "Cannot add alpha mask: a mask has already been specified for this frame!"; + goto lCleanup; + } + value = GET_ARG(1); + //printf("frame count: %d\n",framecount); + //printf("Load sprite '%s'...\n", value); + index = loadsprite(value, offset.x, offset.y, PIXEL_8); //don't use palette for the mask + maskindex = index; + break; + case CMD_MODEL_FLIPFRAME: + newanim->flipframe = GET_FRAME_ARG(1); + break; + case CMD_MODEL_FOLLOWANIM: + newanim->followup.animation = GET_INT_ARG(1); + if(newanim->followup.animation > max_follows) + { + newanim->followup.animation = max_follows; + } + break; + case CMD_MODEL_FOLLOWCOND: + + // 2019-11-24 + // Caskey, Damon V. + // + // Follow up condition is now handled by bitwise logic. + // We need to set up our bit flags based on the old int + // arg for backward compatability. + + // Get legacy style condition arg. + tempInt = GET_INT_ARG(1); + + // Make sure condition is reset. + newanim->followup.condition = FOLLOW_CONDITION_NONE; + + switch (tempInt) + { + default: + case FOLLOW_CONDITION_CMD_READ_ALWAYS: + newanim->followup.condition |= FOLLOW_CONDITION_ANY; + break; + case FOLLOW_CONDITION_CMD_READ_HOSTILE: + newanim->followup.condition |= FOLLOW_CONDITION_HOSTILE_TARGET_TRUE; + break; + case FOLLOW_CONDITION_CMD_READ_HOSTILE_NOKILL_NOBLOCK: + newanim->followup.condition |= FOLLOW_CONDITION_HOSTILE_TARGET_TRUE; + newanim->followup.condition |= FOLLOW_CONDITION_BLOCK_FALSE; + newanim->followup.condition |= FOLLOW_CONDITION_LETHAL_FALSE; + break; + case FOLLOW_CONDITION_CMD_READ_HOSTILE_NOKILL_NOBLOCK_NOGRAB: + newanim->followup.condition |= FOLLOW_CONDITION_HOSTILE_TARGET_TRUE; + newanim->followup.condition |= FOLLOW_CONDITION_BLOCK_FALSE; + newanim->followup.condition |= FOLLOW_CONDITION_GRAB_TRUE; + newanim->followup.condition |= FOLLOW_CONDITION_LETHAL_FALSE; + break; + case FOLLOW_CONDITION_CMD_READ_HOSTILE_NOKILL_BLOCK: + newanim->followup.condition |= FOLLOW_CONDITION_HOSTILE_TARGET_TRUE; + newanim->followup.condition |= FOLLOW_CONDITION_BLOCK_TRUE; + newanim->followup.condition |= FOLLOW_CONDITION_LETHAL_FALSE; + break; + } + + case CMD_MODEL_COUNTERRANGE: + newanim->counter_action.frame.min = GET_FRAME_ARG(1); + newanim->counter_action.frame.max = GET_FRAME_ARG(2); + + // 2019-12-04 + // Caskey, Damon V. + // + // Counter action condition is now handled by bitwise logic. + // We need to set up our bit flags based on the old int + // arg for backward compatability. + + // Get legacy style condition arg. + tempInt = GET_INT_ARG(3); + + // Make sure condition is reset. + newanim->counter_action.condition = COUNTER_ACTION_CONDITION_NONE; + + switch (tempInt) + { + default: + case COUNTER_ACTION_CONDITION_CMD_READ_ALWAYS: + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_DAMAGE_LETHAL_FALSE; + break; + case COUNTER_ACTION_CONDITION_CMD_READ_HOSTILE: + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_DAMAGE_LETHAL_FALSE; + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_HOSTILE_TARGET_TRUE; + break; + case COUNTER_ACTION_CONDITION_CMD_READ_HOSTILE_FRONT_NOFREEZE: + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_BACK_TRUE; + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_BLOCK_TRUE; + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_DAMAGE_LETHAL_FALSE; + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_FREEZE_FALSE; + newanim->counter_action.condition |= COUNTER_ACTION_CONDITION_HOSTILE_TARGET_TRUE; + break; + case COUNTER_ACTION_CONDITION_CMD_READ_ALWAYS_RAGE: + newanim->counter_action.condition ^= FOLLOW_CONDITION_ANY; + break; + } + + newanim->counter_action.damaged = GET_INT_ARG(4); + break; + case CMD_MODEL_WEAPONFRAME: + if(!newanim->weaponframe) + { + newanim->weaponframe = malloc(sizeof(*newanim->weaponframe) * 3); + } + newanim->weaponframe[0] = GET_FRAME_ARG(1); + newanim->weaponframe[1] = GET_INT_ARG(2); + newanim->weaponframe[2] = GET_INT_ARG(3); + break; + case CMD_MODEL_QUAKEFRAME: + newanim->quakeframe.framestart = GET_FRAME_ARG(1); + newanim->quakeframe.repeat = GET_INT_ARG(2); + newanim->quakeframe.v = GET_INT_ARG(3); + newanim->quakeframe.cnt = 0; + break; + case CMD_MODEL_SUBENTITY: + case CMD_MODEL_CUSTENTITY: + value = GET_ARG(1); + if(value[0]) + { + newanim->sub_entity_model_index = get_cached_model_index(value); + } + break; + case CMD_MODEL_SPAWNFRAME: + + newanim->sub_entity_spawn = allocate_sub_entity(); + + newanim->sub_entity_spawn->frame = GET_FRAME_ARG(1); + newanim->sub_entity_spawn->position.x = GET_FLOAT_ARG(2); + newanim->sub_entity_spawn->position.z = GET_FLOAT_ARG(3); + newanim->sub_entity_spawn->position.y = GET_FLOAT_ARG(4); + newanim->sub_entity_spawn->placement = GET_INT_ARG(5); + break; + + case CMD_MODEL_SUMMONFRAME: + + newanim->sub_entity_summon = allocate_sub_entity(); + + newanim->sub_entity_summon->frame = GET_FRAME_ARG(1); + newanim->sub_entity_summon->position.x = GET_FLOAT_ARG(2); + newanim->sub_entity_summon->position.z = GET_FLOAT_ARG(3); + newanim->sub_entity_summon->position.y = GET_FLOAT_ARG(4); + newanim->sub_entity_summon->placement = GET_INT_ARG(5); + break; + + + case CMD_MODEL_UNSUMMONFRAME: + newanim->sub_entity_unsummon = GET_FRAME_ARG(1); + break; + case CMD_MODEL_NOHITHEAD: + + if (GET_INT_ARG(1)) + { + newchar->move_config_flags |= MOVE_CONFIG_NO_HIT_HEAD; + } + else + { + newchar->move_config_flags &= ~MOVE_CONFIG_NO_HIT_HEAD; + } + + break; + case CMD_MODEL_AT_SCRIPT: + at_cmd_mergeable = false; + + if(!scriptbuf[0]) // if empty, paste the main function text here + { + buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen); + } + scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars + scriptlen = strlen(scriptbuf); + if(ani_id >= 0) + { + if(script_id != ani_id) // if expression 1 + { + sprintf(namebuf, ifid_text, newanim->index); + buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen); + script_id = ani_id; + } + scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars + scriptlen = strlen(scriptbuf); + } + while(!starts_with(buf + pos, "@script")) + { + pos++; + } + pos += strclen("@script"); + len = 0; + while(!starts_with(buf + pos, "@end_script")) + { + len++; + pos++; + } + buffer_append(&scriptbuf, buf + pos - len, len, &sbsize, &scriptlen); + pos += strclen("@end_script"); + + if(ani_id >= 0) + { + buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen);// put back last chars + } + buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen);// put back last chars + break; + case CMD_MODEL_AT_CMD: + { + s_command_token command_token; + s_command_token_reader command_token_reader; + bool command_emitted = false; + bool first_command_argument; + + //translate @cmd into script function call + if(ani_id < 0) + { + shutdownmessage = "command '@cmd' must follow an animation!"; + goto lCleanup; + } + if(!scriptbuf[0]) // if empty, paste the main function text here + { + buffer_append(&scriptbuf, pre_text, 0xffffff, &sbsize, &scriptlen); + } + scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars + scriptlen = strlen(scriptbuf); + if(script_id != ani_id) // if expression 1 + { + at_cmd_mergeable = false; + sprintf(namebuf, ifid_text, newanim->index); + buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen); + script_id = ani_id; + } + scriptbuf[scriptlen - strclen(endifid_text)] = 0; // cut last chars + scriptlen = strlen(scriptbuf); + command_token_reader = (s_command_token_reader){ + .cursor = buf + pos + }; + + /* Skip @cmd, then read the function name. */ + if(command_token_reader_next(&command_token_reader, &command_token) + && command_token_reader_next(&command_token_reader, &command_token)) + { + /* + //no_cmd_compatible will try to optimize if(frame==n) + //which means merging extra if statements within the same frame + //some old mod will have problem if this is enabled, however. + // + // @cmd f + // @cmd f + // frame + // + // When no_cmd_compatible is 1 + // + // if(frame==n) { + // f(); + // f(); + // return; + // } + // + // When no_cmd_compatible is 0 + // + // if(frame==n) { + // f(); + // } + // if(frame==n) { + // f(); + // } + */ + /* + - Caskey, Damon V. + - 2026-08-12 + - + - Merge same-frame @cmd calls only when the + previous generated section is an eligible + @cmd block with the exact expected suffix. + Otherwise, open a new frame condition without + removing any existing script text. + */ + const size_t frame_close_length = strclen(endif_text); + const size_t frame_return_length = strclen(endif_return_text); + + bool merge_previous_command = + no_cmd_compatible + && at_cmd_mergeable + && frm_id == curframe; + + if(merge_previous_command) + { + merge_previous_command = buffer_remove_suffix_pair( + scriptbuf, + &scriptlen, + endif_return_text, + frame_return_length, + endif_text, + frame_close_length + ); + } + + if(!merge_previous_command) + { + sprintf(namebuf, if_text, curframe);//only execute in current frame + buffer_append(&scriptbuf, namebuf, 0xffffff, &sbsize, &scriptlen); + frm_id = curframe; + } + buffer_append(&scriptbuf, call_indent_text, 0xffffff, &sbsize, &scriptlen); + buffer_append( + &scriptbuf, + command_token.text, + command_token.length, + &sbsize, + &scriptlen + ); + buffer_append(&scriptbuf, call_open_text, 0xffffff, &sbsize, &scriptlen); + + first_command_argument = true; + while(command_token_reader_next( + &command_token_reader, + &command_token + )) + { + if(!first_command_argument) + { + buffer_append(&scriptbuf, comma_text, 0xffffff, &sbsize, &scriptlen); + } + buffer_append( + &scriptbuf, + command_token.text, + command_token.length, + &sbsize, + &scriptlen + ); + first_command_argument = false; + } + + command_emitted = true; + } + + if(command_token_reader.unterminated_quote) + { + snprintf( + alert_buffer, + sizeof(alert_buffer), + "Command '@cmd' has an unterminated %c quote.\n", + command_token_reader.unterminated_quote + ); + + shutdownmessage = alert_buffer; + goto lCleanup; + } + + buffer_append(&scriptbuf, endcall_text, 0xffffff, &sbsize, &scriptlen); + if(no_cmd_compatible) + { + buffer_append(&scriptbuf, endif_return_text, 0xffffff, &sbsize, &scriptlen); //return + } + buffer_append(&scriptbuf, endif_text, 0xffffff, &sbsize, &scriptlen);//end of if + buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen); // put back last chars + buffer_append(&scriptbuf, sur_text, 0xffffff, &sbsize, &scriptlen); // put back last chars + at_cmd_mergeable = no_cmd_compatible && command_emitted; + break; + } + default: + if(command && command[0]) + { + if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size)) + { + printf("Command '%s' not understood in file '%s'!\n", command, filename); + } + } + } + + } + // Go to next line + pos += getNewLineStart(buf + pos); + } + + + tempInt = 1; + + if(scriptbuf && animscriptbuf && scriptbuf[0] && animscriptbuf[0]) + { + writeToScriptLog("\n#### animationscript function main #####\n# "); + writeToScriptLog(filename); + writeToScriptLog("\n########################################\n"); + writeToScriptLog(scriptbuf); + + lcmScriptDeleteMain(&scriptbuf); + lcmScriptAddMain(&animscriptbuf); + lcmScriptJoinMain(&animscriptbuf,scriptbuf); + + if(!Script_IsInitialized(newchar->scripts->animation_script)) + { + Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0); + } + tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename); + } + else if(animscriptbuf && animscriptbuf[0]) + { + lcmScriptAddMain(&animscriptbuf); + + if(!Script_IsInitialized(newchar->scripts->animation_script)) + { + Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0); + } + tempInt = Script_AppendText(newchar->scripts->animation_script, animscriptbuf, filename); + } + else if(scriptbuf && scriptbuf[0]) + { + //printf("\n%s\n", scriptbuf); + if(!Script_IsInitialized(newchar->scripts->animation_script)) + { + Script_Init(newchar->scripts->animation_script, newchar->name, filename, 0); + } + tempInt = Script_AppendText(newchar->scripts->animation_script, scriptbuf, filename); + //Interpreter_OutputPCode(newchar->scripts->animation_script.pinterpreter, "code"); + writeToScriptLog("\n#### animationscript function main #####\n# "); + writeToScriptLog(filename); + writeToScriptLog("\n########################################\n"); + writeToScriptLog(scriptbuf); + } + + if(!newchar->isSubclassed) + { + Script_Compile(newchar->scripts->animation_script); + } + + if(!tempInt)// parse script failed + { + shutdownmessage = "Error parsing function main of animation script in file '%s'!"; + goto lCleanup; + } + + // We need a little more work to initialize the new A.I. types if they are not loaded from file + if(newchar->aiattack == -1) + { + newchar->aiattack = 0; + } + if(newchar->aimove == AIMOVE1_NONE) + { + newchar->aimove = AIMOVE1_NORMAL; + } + //if(!newchar->offscreenkill) newchar->offscreenkill = 1000; + + //temporary patch for conflicting moves + if(newchar->animation[ANI_FREESPECIAL] && !is_set(newchar, ANI_FREESPECIAL)) + { + if(!alloc_specials(newchar)) + { + shutdownmessage = E_OUT_OF_MEMORY; + goto lCleanup; + } + + newchar->special[newchar->specials_loaded].input[0].press = FLAG_FORWARD; + newchar->special[newchar->specials_loaded].input[1].press = FLAG_FORWARD; + newchar->special[newchar->specials_loaded].input[2].press = FLAG_ATTACK; + newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL; + newchar->special[newchar->specials_loaded].steps = 3; + newchar->specials_loaded++; + } + if(newchar->animation[ANI_FREESPECIAL2] && !is_set(newchar, ANI_FREESPECIAL2)) + { + if(!alloc_specials(newchar)) + { + shutdownmessage = E_OUT_OF_MEMORY; + goto lCleanup; + } + + newchar->special[newchar->specials_loaded].input[0].press = FLAG_MOVEDOWN; + newchar->special[newchar->specials_loaded].input[1].press = FLAG_MOVEDOWN; + newchar->special[newchar->specials_loaded].input[2].press = FLAG_ATTACK; + newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL2; + newchar->special[newchar->specials_loaded].steps = 3; + newchar->specials_loaded++; + } + if(newchar->animation[ANI_FREESPECIAL3] && !is_set(newchar, ANI_FREESPECIAL3)) + { + if(!alloc_specials(newchar)) + { + shutdownmessage = E_OUT_OF_MEMORY; + goto lCleanup; + } + + newchar->special[newchar->specials_loaded].input[0].press = FLAG_MOVEUP; + newchar->special[newchar->specials_loaded].input[1].press = FLAG_MOVEUP; + newchar->special[newchar->specials_loaded].input[2].press = FLAG_ATTACK; + newchar->special[newchar->specials_loaded].anim = ANI_FREESPECIAL3; + newchar->special[newchar->specials_loaded].steps = 3; + newchar->specials_loaded++; + } + + if(newchar->risetime.rise == -1) + { + if(newchar->type == TYPE_PLAYER) + { + if(newchar->animation[ANI_RISEATTACK]) + { + newchar->risetime.rise = global_config.game_speed / 2; + } + else + { + newchar->risetime.rise = global_config.game_speed; + } + } + else if(newchar->type == TYPE_ENEMY || newchar->type == TYPE_NPC) + { + newchar->risetime.rise = 0; + } + } + + if(newchar->faction.type_hostile == TYPE_UNDECLARED) // not been initialized, so initialize it + { + switch (newchar->type) + { + default: + //Do nothing. + break; + case TYPE_ENEMY: + newchar->faction.type_hostile = TYPE_PLAYER; + break; + case TYPE_PLAYER: // dont really needed, since you don't need A.I. control for players + newchar->faction.type_hostile = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE; + break; + case TYPE_TRAP: + newchar->faction.type_hostile = TYPE_ENEMY | TYPE_PLAYER; + case TYPE_OBSTACLE: + newchar->faction.type_hostile = TYPE_UNDECLARED; + break; + case TYPE_PROJECTILE: + // We want a clean slate so the projectile + // spawn functions will copy owner settings + // by default. + newchar->faction.type_hostile = TYPE_UNDECLARED; + break; + case TYPE_SHOT: // only target enemies + newchar->faction.type_hostile = TYPE_ENEMY ; + break; + case TYPE_NPC: // default npc behivior + newchar->faction.type_hostile = TYPE_ENEMY ; + break; + } + } + + if(newchar->faction.type_damage_direct == TYPE_UNDECLARED) // not been initialized, so initialize it + { + switch (newchar->type) + { + default: + //Do nothing. + break; + case TYPE_ENEMY: + newchar->faction.type_damage_direct = TYPE_PLAYER | TYPE_SHOT; + if(newchar->subtype == SUBTYPE_ARROW) + { + newchar->faction.type_damage_direct |= TYPE_OBSTACLE; + } + break; + case TYPE_PLAYER: + newchar->faction.type_damage_direct = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE; + break; + case TYPE_TRAP: + newchar->faction.type_damage_direct = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + case TYPE_OBSTACLE: + newchar->faction.type_damage_direct = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE; + break; + case TYPE_PROJECTILE: + // We want a clean slate so the projectile + // spawn functions will copy owner settings + // by default. + newchar->faction.type_damage_direct = TYPE_UNDECLARED; + break; + case TYPE_SHOT: + newchar->faction.type_damage_direct = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + break; + case TYPE_NPC: + newchar->faction.type_damage_direct = TYPE_ENEMY | TYPE_OBSTACLE; + break; + case TYPE_ITEM: + newchar->faction.type_damage_direct = TYPE_PLAYER; + break; + } + } + + if(newchar->faction.type_damage_indirect == TYPE_UNDECLARED) // not been initialized, so initialize it + { + switch (newchar->type) + { + default: + //Do nothing. + break; + case TYPE_ENEMY: + newchar->faction.type_damage_indirect = TYPE_ENEMY | TYPE_OBSTACLE; + break; + case TYPE_PLAYER: + newchar->faction.type_damage_indirect = TYPE_PLAYER | TYPE_OBSTACLE; + break; + case TYPE_TRAP: // hmm, don't really needed + newchar->faction.type_damage_indirect = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + case TYPE_OBSTACLE: // hmm, don't really needed + newchar->faction.type_damage_indirect = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + break; + case TYPE_PROJECTILE: + // We want a clean slate so the projectile + // spawn functions will copy owner settings + // by default. + newchar->faction.type_damage_indirect = TYPE_UNDECLARED; + break; + case TYPE_SHOT: // hmm, don't really needed + newchar->faction.type_damage_indirect = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + break; + case TYPE_NPC: + newchar->faction.type_damage_indirect = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE; + break; + } + } + + if(newchar->jumpspeed < 0) + { + newchar->jumpspeed = MAX(newchar->speed.x, 1); + } + + if(blendfx_is_set == 0) + { + if(newchar->alpha) + { + blendfx[newchar->alpha - 1] = 1; + } + if(newchar->shadow_config_flags & SHADOW_CONFIG_GRAPHIC_ALL || newchar->shadow) + { + blendfx[BLEND_MULTIPLY] = 1; + } + } + + // we need to convert 8bit colourmap into 32bit palette + if(pixelformat == PIXEL_x8) + { + convert_map_to_palette(newchar, mapflag); + } + +lCleanup: + + if(buf != NULL) + { + free(buf); + buf = NULL; + } + if(scriptbuf) + { + free(scriptbuf); + scriptbuf = NULL; + } + if(animscriptbuf) + { + free(animscriptbuf); + animscriptbuf = NULL; + } + if(mapflag) + { + free(mapflag); + mapflag = NULL; + } + + /* + * Free parser scratch collision collections. + * + * These collections are temporary carry-forward state + * used while reading model commands. Real animation + * frames receive cloned collections when frames are + * added, so the parser scratch collections should not + * survive model loading. + */ + collision_collection_free(temp_collision_attack); + temp_collision_attack = NULL; + + collision_collection_free(temp_collision_body); + temp_collision_body = NULL; + + collision_collection_free(temp_collision_space); + temp_collision_space = NULL; + + frame_sound_collection_free(temp_frame_sound); + temp_frame_sound = NULL; + + child_spawn_free_list(temp_child_spawn_head); + temp_child_spawn_head = NULL; + + if(!shutdownmessage) + { + model_cache[cacheindex].lifecycle = MODEL_LIFECYCLE_LOAD_EVENT; + execute_model_load_scripts(newchar); + model_cache[cacheindex].lifecycle = MODEL_LIFECYCLE_LOADED; + printf(" ...done!\n"); + return newchar; + } + + borShutdown(1, "Fatal Error in load_cached_model, file: %s, line %d, message: %s\n", filename, line, shutdownmessage); + return NULL; + + #undef LOG_CMD_TITLE +} + + + +int is_set(s_model *model, int m) // New function to determine if a freespecial has been set +{ + int i; + + for(i = 0; i < model->specials_loaded; i++) + { + if(model->special[i].anim == m) + { + return 1; + } + } + + return 0; +} + +int load_script_setting() +{ + char *filename = "data/script.txt"; + char *buf, *command; + ptrdiff_t pos = 0; + size_t size = 0; + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + + if(buffer_pakfile(filename, &buf, &size) != 1) + { + return 0; + } + + while(pos < size) + { + if(ParseArgs(&arglist, buf + pos, argbuf)) + { + command = GET_ARG(0); + if(command && command[0]) + { + if(stricmp(command, "maxscriptvars") == 0) // each script can have a variable list that can be accessed by index + { + max_script_vars = GET_INT_ARG(1) ; + if(max_script_vars < 0) + { + max_script_vars = 0; + } + } + else if(stricmp(command, "maxentityvars") == 0) // each entity can have a variable list that can be accessed by index + { + max_entity_vars = GET_INT_ARG(1) ; + if(max_entity_vars < 0) + { + max_entity_vars = 0; + } + } + else if(stricmp(command, "maxindexedvars") == 0) // a global variable list that can be accessed by index + { + max_indexed_vars = GET_INT_ARG(1); + if(max_indexed_vars < 0) + { + max_indexed_vars = 0; + } + } + else if(stricmp(command, "keyscriptrate") == 0) // Rate that keyscripts fire when holding a key. + { + keyscriptrate = GET_INT_ARG(1); + } + else if(stricmp(command, "alwaysupdate") == 0) //execute update script whenever update() is called + { + alwaysupdate = GET_INT_ARG(1); + } + else if(stricmp(command, "nonestedscript") == 0) // don't call a script if it is being executed + { + no_nested_script = GET_INT_ARG(1); + } + else if(stricmp(command, "nocmdcompatible") == 0) // don't call a script if it is being executed + { + no_cmd_compatible = GET_INT_ARG(1); + } + } + } + // Go to next line + pos += getNewLineStart(buf + pos); + } + + if(buf != NULL) + { + free(buf); + buf = NULL; + } + return 1; +} + +/* +* Convert one models.txt limit to an int without signed +* overflow or partial numeric conversion. +*/ +static bool model_constant_get_unsigned_int(const char* text, int* result) +{ + s_command_token token; + uint64_t parsed_value; + + assert(text); + assert(result); + + token.text = text; + token.length = strlen(text); + + if(!command_token_get_uint64(&token, &parsed_value) + || parsed_value > (uint64_t)INT_MAX) { + return false; + } + + *result = (int)parsed_value; + + return true; +} + +/* +* Allocate a models.txt-driven table only after checking +* its multiplication. Allocation failure is fatal because +* model loading cannot safely continue with a missing table. +*/ +static void* model_constant_array_allocate( + const size_t element_count, + const size_t element_size, + const char* table_name, + const char* filename +) { + void* result; + + assert(table_name); + assert(filename); + + if(element_count == 0 + || element_size == 0 + || element_count > SIZE_MAX / element_size) { + borShutdown( + 1, + "Invalid allocation size for %s while loading %s.\n", + table_name, + filename + ); + + return NULL; + } + + result = malloc(element_count * element_size); + + if(!result) { + borShutdown( + 1, + "Unable to allocate %zu entries for %s while loading %s.\n", + element_count, + table_name, + filename + ); + + return NULL; + } + + return result; +} + +void load_model_constants() +{ + char filename[MAX_BUFFER_LEN] = "data/models.txt"; + int i; + int64_t calculated_max_animations; + char *buf; + size_t size; + ptrdiff_t pos; + char *command; + int line = 0; + int maxanim = MAX_ANIS; // temporary counter + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + modelstxtCommands cmd; + + // reload default values + max_idles = MAX_IDLES; + max_walks = MAX_WALKS; + max_ups = MAX_UPS; + max_downs = MAX_DOWNS; + max_backwalks = MAX_BACKWALKS; + max_attack_types = MAX_ATKS; + max_freespecials = MAX_SPECIALS; + max_follows = MAX_FOLLOWS; + max_attacks = MAX_ATTACKS; + max_animations = MAX_ANIS; + + max_collisons = MAX_COLLISIONS; + + // free old values + if(animspecials) + { + free(animspecials); + animspecials = NULL; + } + if(animattacks) + { + free(animattacks); + animattacks = NULL; + } + if(animfollows) + { + free(animfollows); + animfollows = NULL; + } + if(animpains) + { + free(animpains); + animpains = NULL; + } + if(animbackpains) + { + free(animbackpains); + animbackpains = NULL; + } + if(animfalls) + { + free(animfalls); + animfalls = NULL; + } + if(animbackfalls) + { + free(animbackfalls); + animbackfalls = NULL; + } + if(animrises) + { + free(animrises); + animrises = NULL; + } + if(animbackrises) + { + free(animbackrises); + animbackrises = NULL; + } + if(animriseattacks) + { + free(animriseattacks); + animriseattacks = NULL; + } + if(animbackriseattacks) + { + free(animbackriseattacks); + animbackriseattacks = NULL; + } + if(animblkpains) + { + free(animblkpains); + animblkpains = NULL; + } + if(animbackblkpains) + { + free(animbackblkpains); + animbackblkpains = NULL; + } + if(animdies) + { + free(animdies); + animdies = NULL; + } + if(animbackdies) + { + free(animbackdies); + animbackdies = NULL; + } + if(animwalks) + { + free(animwalks); + animwalks = NULL; + } + if(animbackwalks) + { + free(animbackwalks); + animbackwalks = NULL; + } + if(animidles) + { + free(animidles); + animidles = NULL; + } + if(animups) + { + free(animups); + animups = NULL; + } + if(animdowns) + { + free(animdowns); + animdowns = NULL; + } + if(ai_attack_choices) + { + free(ai_attack_choices); + ai_attack_choices = NULL; + ai_attack_choice_capacity = 0; + } + + // Read file + if(buffer_pakfile(filename, &buf, &size) != 1) + { + borShutdown(1, "Error loading model list from %s", filename); + } + + pos = 0; + while(pos < size) // peek global settings + { + line++; + if(ParseArgs(&arglist, buf + pos, argbuf)) + { + command = GET_ARG(0); + cmd = (modelstxtCommands)getModelCommand(modelstxtcmdlist, command); + switch(cmd) + { + case CMD_MODELSTXT_MAX_COLLISIONS: + // max collision_attack boxes. + max_collisons = GET_INT_ARG(1); + if(max_collisons < MAX_COLLISIONS) + { + max_collisons = MAX_COLLISIONS; + } + break; + case CMD_MODELSTXT_MAXIDLES: + // max idle stances + max_idles = GET_INT_ARG(1); + if(max_idles < MAX_IDLES) + { + max_idles = MAX_IDLES; + } + break; + case CMD_MODELSTXT_MAXWALKS: + max_walks = GET_INT_ARG(1); + if(max_walks < MAX_WALKS) + { + max_walks = MAX_WALKS; + } + break; + case CMD_MODELSTXT_MAXBACKWALKS: + // max backward walks + max_backwalks = GET_INT_ARG(1); + if(max_backwalks < MAX_BACKWALKS) + { + max_backwalks = MAX_BACKWALKS; + } + break; + case CMD_MODELSTXT_MAXUPS: + // max up walks + max_ups = GET_INT_ARG(1); + if(max_ups < MAX_UPS) + { + max_ups = MAX_UPS; + } + break; + case CMD_MODELSTXT_MAXDOWNS: + // max down walks + max_downs = GET_INT_ARG(1); + if(max_downs < MAX_DOWNS) + { + max_downs = MAX_DOWNS; + } + break; + case CMD_MODELSTXT_MAXATTACKTYPES: + // max attacktype/pain/fall/die + max_attack_types = GET_INT_ARG(1) + STA_ATKS; + if(max_attack_types < MAX_ATKS) + { + max_attack_types = MAX_ATKS; + } + break; + case CMD_MODELSTXT_MAXFOLLOWS: + // max follow-ups + max_follows = GET_INT_ARG(1); + if(max_follows < MAX_FOLLOWS) + { + max_follows = MAX_FOLLOWS; + } + break; + case CMD_MODELSTXT_MAXFREESPECIALS: + // max freespecials + if(!model_constant_get_unsigned_int( + GET_ARG(1), + &max_freespecials + )) { + borShutdown( + 1, + "Invalid maxfreespecials value '%s' in %s, line %d. " + "Expected an unsigned integer no greater than %d.\n", + GET_ARG(1), + filename, + line, + INT_MAX + ); + } + + if(max_freespecials < MAX_SPECIALS) + { + max_freespecials = MAX_SPECIALS; + } + break; + case CMD_MODELSTXT_MAXATTACKS: + max_attacks = GET_INT_ARG(1); + if(max_attacks < MAX_ATTACKS) + { + max_attacks = MAX_ATTACKS; + } + break; + default: + if(cmd >= CMD_MODELSTXT_THE_END) + { + printf("command %s not understood in %s, line %d\n", command, filename, line); + } + break; + } + } + + // Go to next line + pos += getNewLineStart(buf + pos); + } + + /* + * Calculate in a wider type before assigning the int + * animation identifiers used throughout the engine. + */ + calculated_max_animations = + (int64_t)MAX_ANIS + + ((int64_t)max_attack_types - MAX_ATKS) * INT64_C(12) + + ((int64_t)max_follows - MAX_FOLLOWS) + + ((int64_t)max_freespecials - MAX_SPECIALS) + + ((int64_t)max_attacks - MAX_ATTACKS) + + ((int64_t)max_idles - MAX_IDLES) + + ((int64_t)max_walks - MAX_WALKS) + + ((int64_t)max_ups - MAX_UPS) + + ((int64_t)max_downs - MAX_DOWNS) + + ((int64_t)max_backwalks - MAX_BACKWALKS); + + if(calculated_max_animations < MAX_ANIS + || calculated_max_animations > INT_MAX) { + borShutdown( + 1, + "Configured model animation limits require %" PRId64 + " animation identifiers, but the supported maximum is %d " + "while loading %s.\n", + calculated_max_animations, + INT_MAX, + filename + ); + } + + max_animations = (int)calculated_max_animations; + + // alloc indexed animation ids + animdowns = model_constant_array_allocate((size_t)max_downs, sizeof(*animdowns), "down animation identifiers", filename); + animups = model_constant_array_allocate((size_t)max_ups, sizeof(*animups), "up animation identifiers", filename); + animbackwalks = model_constant_array_allocate((size_t)max_backwalks, sizeof(*animbackwalks), "backwalk animation identifiers", filename); + animwalks = model_constant_array_allocate((size_t)max_walks, sizeof(*animwalks), "walk animation identifiers", filename); + animidles = model_constant_array_allocate((size_t)max_idles, sizeof(*animidles), "idle animation identifiers", filename); + animpains = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animpains), "pain animation identifiers", filename); + animbackpains = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackpains), "back-pain animation identifiers", filename); + animdies = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animdies), "death animation identifiers", filename); + animbackdies = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackdies), "back-death animation identifiers", filename); + animfalls = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animfalls), "fall animation identifiers", filename); + animbackfalls = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackfalls), "back-fall animation identifiers", filename); + animrises = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animrises), "rise animation identifiers", filename); + animbackrises = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackrises), "back-rise animation identifiers", filename); + animriseattacks = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animriseattacks), "rise-attack animation identifiers", filename); + animbackriseattacks = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackriseattacks), "back-rise-attack animation identifiers", filename); + animblkpains = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animblkpains), "block-pain animation identifiers", filename); + animbackblkpains = model_constant_array_allocate((size_t)max_attack_types, sizeof(*animbackblkpains), "back-block-pain animation identifiers", filename); + animattacks = model_constant_array_allocate((size_t)max_attacks, sizeof(*animattacks), "attack animation identifiers", filename); + animfollows = model_constant_array_allocate((size_t)max_follows, sizeof(*animfollows), "follow animation identifiers", filename); + animspecials = model_constant_array_allocate((size_t)max_freespecials, sizeof(*animspecials), "freespecial animation identifiers", filename); + ai_attack_choice_capacity = (size_t)max_animations; + ai_attack_choices = model_constant_array_allocate(ai_attack_choice_capacity, sizeof(*ai_attack_choices), "AI attack-choice table", filename); + + // copy default values and new animation ids + memcpy(animdowns, downs, sizeof(*animdowns)*MAX_DOWNS); + for(i = MAX_DOWNS; i < max_downs; i++) + { + animdowns[i] = maxanim++; + } + memcpy(animups, ups, sizeof(*animups)*MAX_UPS); + for(i = MAX_UPS; i < max_ups; i++) + { + animups[i] = maxanim++; + } + memcpy(animbackwalks, backwalks, sizeof(*animbackwalks)*MAX_BACKWALKS); + for(i = MAX_BACKWALKS; i < max_backwalks; i++) + { + animbackwalks[i] = maxanim++; + } + memcpy(animwalks, walks, sizeof(*animwalks)*MAX_WALKS); + for(i = MAX_WALKS; i < max_walks; i++) + { + animwalks[i] = maxanim++; + } + memcpy(animidles, idles, sizeof(*animidles)*MAX_IDLES); + for(i = MAX_IDLES; i < max_idles; i++) + { + animidles[i] = maxanim++; + } + memcpy(animspecials, freespecials, sizeof(*animspecials)*MAX_SPECIALS); + for(i = MAX_SPECIALS; i < max_freespecials; i++) + { + animspecials[i] = maxanim++; + } + memcpy(animattacks, normal_attacks, sizeof(*animattacks)*MAX_ATTACKS); + for(i = MAX_ATTACKS; i < max_attacks; i++) + { + animattacks[i] = maxanim++; + } + memcpy(animfollows, follows, sizeof(*animfollows)*MAX_FOLLOWS); + for(i = MAX_FOLLOWS; i < max_follows; i++) + { + animfollows[i] = maxanim++; + } + memcpy(animpains, pains, sizeof(*animpains)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + //printf("\nInitializing animpains[%d]: setting to new anim ID %d", i, maxanim); + animpains[i] = maxanim++; + } + memcpy(animbackpains, backpains, sizeof(*animbackpains)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackpains[i] = maxanim++; + } + memcpy(animfalls, falls, sizeof(*animfalls)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animfalls[i] = maxanim++; + } + memcpy(animbackfalls, backfalls, sizeof(*animbackfalls)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackfalls[i] = maxanim++; + } + memcpy(animrises, rises, sizeof(*animrises)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animrises[i] = maxanim++; + } + memcpy(animbackrises, backrises, sizeof(*animbackrises)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackrises[i] = maxanim++; + } + memcpy(animriseattacks, riseattacks, sizeof(*animriseattacks)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animriseattacks[i] = maxanim++; + } + memcpy(animbackriseattacks, backriseattacks, sizeof(*animbackriseattacks)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackriseattacks[i] = maxanim++; + } + memcpy(animblkpains, blkpains, sizeof(*animblkpains)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animblkpains[i] = maxanim++; + } + memcpy(animbackblkpains, backblkpains, sizeof(*animbackblkpains)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackblkpains[i] = maxanim++; + } + memcpy(animdies, deaths, sizeof(*animdies)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animdies[i] = maxanim++; + } + memcpy(animbackdies, backdeaths, sizeof(*animbackdies)*MAX_ATKS); + for(i = MAX_ATKS; i < max_attack_types; i++) + { + animbackdies[i] = maxanim++; + } + + if(buf) + { + free(buf); + } +} + +// Load / cache all models +int load_models() +{ + char filename[MAX_BUFFER_LEN] = "data/models.txt"; + int i; + char *buf; + size_t size; + ptrdiff_t pos; + char *command; + int line = 0; + + char tmpBuff[MAX_BUFFER_LEN] = {""}; + + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + modelstxtCommands cmd; + int modelLoadCount = 0; + + char* value = NULL; + int tempInt = 0; + + const char* delay_mode_error; + + free_modelcache(); + + /* + * Reset load-only configuration before reading models.txt + * so a reload cannot retain a setting that was removed. + */ + global_config.delay_unit = DELAY_UNIT_CENTISECOND; + + if(isLoadingScreenTypeBg(loadingbg[0].set)) + { + // New alternative background path. + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "loading"); + load_background(tmpBuff); + } + else + { + load_background("data/bgs/loading"); + } + standard_palette(1); + } + if(isLoadingScreenTypeBar(loadingbg[0].set)) + { + lifebar_colors(); + init_colourtable(); + } + + update_loading(&loadingbg[0], -1, 1); // initialize the update screen + + if(custModels != NULL) + { + strcpy(filename, "data/"); + strcat(filename, custModels); + } + + // Read file + if(buffer_pakfile(filename, &buf, &size) != 1) + { + borShutdown(1, "Error loading model list from %s", filename); + } + + pos = 0; + while(pos < size) // peek global settings + { + line++; + if(ParseArgs(&arglist, buf + pos, argbuf)) + { + command = GET_ARG(0); + cmd = (modelstxtCommands)getModelCommand(modelstxtcmdlist, command); + switch(cmd) + { + case CMD_MODELSTXT_COMBODELAY: + combodelay = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_MUSIC: + music(GET_ARG(1), 1, atol(GET_ARG(2))); + break; + case CMD_MODELSTXT_LOAD: + // Add path to cache list + modelLoadCount++; + cache_model(GET_ARG(1), GET_ARG(2), 1); + break; + case CMD_MODELSTXT_COLOURSELECT: + // 6-2-2005 if string for colourselect found + colourselect = GET_INT_ARG(1); // 6-2-2005 + break; + case CMD_MODELSTXT_SPDIRECTION: + // Select Player Direction for select player screen + spdirection[0] = direction_get_direction_from_argument(filename, command, GET_ARG(1)); + spdirection[1] = direction_get_direction_from_argument(filename, command, GET_ARG(2)); + spdirection[2] = direction_get_direction_from_argument(filename, command, GET_ARG(3)); + spdirection[3] = direction_get_direction_from_argument(filename, command, GET_ARG(4)); + break; + case CMD_MODELSTXT_AUTOLAND: + // New flag to determine if a player auto lands when thrown by another player (2 completely disables the ability to land) + autoland = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_NOLOST: + // this is use for dont lost your weapon if you grab a enemy flag it to 1 to no drop by tails + nolost = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_AJSPECIAL: + + value = GET_ARG(1); + + if (stricmp(value, "special") == 0) + { + tempInt = AJSPECIAL_KEY_SPECIAL; + } + else if (stricmp(value, "double") == 0) + { + tempInt = AJSPECIAL_KEY_DOUBLE; + } + else if (stricmp(value, "attack2") == 0) + { + tempInt = AJSPECIAL_KEY_ATTACK2; + } + else if (stricmp(value, "attack3") == 0) + { + tempInt = AJSPECIAL_KEY_ATTACK3; + } + else if (stricmp(value, "attack4") == 0) + { + tempInt = AJSPECIAL_KEY_ATTACK4; + } + else + { + tempInt = GET_INT_ARG(1); + } + + global_config.ajspecial = tempInt; + + break; + case CMD_MODELSTXT_NOCOST: + // Nocost set in models.txt + nocost = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_NOCHEATS: + + /* Disable access to cheats menu. */ + + if (GET_INT_ARG(1)) + { + global_config.cheats &= ~CHEAT_OPTIONS_MASTER_MENU; + } + + break; + + case CMD_MODELSTXT_NODEBUG: + //disable debug option in menu + nodebugoptions = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_NODROPEN: + nodropen = 1; + break; + case CMD_MODELSTXT_NODROPSPAWN: + nodropspawn = 1; + break; + case CMD_MODELSTXT_KNOW: + // Just add path to cache list + cache_model(GET_ARG(1), GET_ARG(2), 0); + break; + case CMD_MODELSTXT_NOAIRCANCEL: + noaircancel = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_NOMAXRUSHRESET: + nomaxrushreset[4] = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_MPBLOCK: + // Take from MP first? + global_config.block_type = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_BLOCKRATIO: + global_config.block_ratio = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_NOCHIPDEATH: + nochipdeath = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_LIFESCORE: + lifescore = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_CREDSCORE: + // Number of points needed to earn a 1-up + credscore = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_DELAY_UNIT: + delay_mode_error = delay_unit_from_text( + GET_ARG(1), + false, + &global_config.delay_unit + ); + + if(delay_mode_error) { + borShutdown( + 1, + "%s Invalid value '%s' in %s, line %d.\n", + delay_mode_error, + GET_ARG(1), + filename, + line + ); + } + break; + case CMD_MODELSTXT_VERSUSDAMAGE: + // Number of points needed to earn a credit + versusdamage = GET_INT_ARG(1); + if(versusdamage == 0 || versusdamage == 1) + { + savedata.mode = versusdamage ^ 1; + } + break; + case CMD_MODELSTXT_DROPV: + default_model_dropv.y = GET_FLOAT_ARG(1); + default_model_dropv.x = GET_FLOAT_ARG(2); + default_model_dropv.z = GET_FLOAT_ARG(3); + break; + case CMD_MODELSTXT_JUMPSPEED: + default_model_jumpspeed = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_JUMPHEIGHT: + default_model_jumpheight = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_CHEATS: + + lcmHandleCommandGlobalConfigCheats(buf + pos); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_FLASH_LAYER_ADJUST: + global_config.flash.layer_adjust = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_FLASH_LAYER_SOURCE: + global_config.flash.layer_source = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_FLASH_Z_SOURCE: + global_config.flash.z_source = GET_INT_ARG(1); + break; + case CMD_MODELSTXT_GLOBAL_CONFIG_GAME_SPEED: + + tempInt = GET_INT_ARG(1); + if (tempInt < 1) { + printf("\nCommand %s %d in %s, line %d value(%d) out of bounds, using default %d.\n", command, tempInt, filename, line, tempInt, GAME_SPEED_DEFAULT); + tempInt = 1; + } + + global_config.game_speed = tempInt; + break; + case CMD_MODELSTXT_GRABDISTANCE: + default_model_grabdistance = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_MNAF: + move_noatk_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_GNAF: + group_noatk_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_ANAF: + agg_noatk_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_MINNA: + min_noatk_chance = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_MAXNA: + max_noatk_chance = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_OSNAF: + offscreen_noatk_factor = GET_FLOAT_ARG(1); + break; + case CMD_MODELSTXT_DEBUG_NAD: + noatk_duration = GET_FLOAT_ARG(1); + break; + default: + if(cmd >= CMD_MODELSTXT_THE_END) + { + printf("command %s not understood in %s, line %d\n", command, filename, line); + } + break; + } + } + + // Go to next line + pos += getNewLineStart(buf + pos); + } + + // Defer load_cached_model, so you can define models after their nested model. + printf("\n"); + + for(i = 0, pos = 0; i < models_cached; i++) + { + //printf("Checking '%s' '%s'\n", model_cache[i].name, model_cache[i].path); + if(stricmp(model_cache[i].name, "global_model") == 0) + { + global_model = i; + } + if(model_cache[i].loadflag) + { + load_cached_model(model_cache[i].name, "models.txt", 0); + update_loading(&loadingbg[0], ++pos, modelLoadCount); + } + } + printf("\nLoading models...............\tDone!\n"); + + + if(buf) + { + free(buf); + } + + return 1; +} + + + + +void unload_levelorder() +{ + int i, j, t; + s_level_entry *le; + s_set_entry *se; + + if(levelsets) + { + for(i = 0; i < num_difficulties; i++) + { + se = levelsets + i; + if(se->name) + { + free(se->name); + } + if(se->numlevels) + { + for(j = 0; j < se->numlevels; j++) + { + le = se->levelorder + j; + if(le->branchname) + { + free(le->branchname); + } + if(le->filename) + { + free(le->filename); + } + //if(le->skipselect) + //{ + for(t = 0; t < MAX_PLAYERS; t++) + { + if(le->skipselect[t]) + { + free(le->skipselect[t]); + } + } + //} + } + free(se->levelorder); + } + } + + free(levelsets); + levelsets = NULL; + } + + num_difficulties = 0; +} + + + +// Add a level to the level order +s_level_entry *add_level(char *filename, s_set_entry *set) +{ + s_level_entry *le = NULL; + int Zs[3] = {0, 0, 0}; + + if(z_coords[0] > 0) + { + Zs[0] = z_coords[0]; + } + else + { + Zs[0] = PLAYER_MIN_Z; + } + + if(z_coords[1] > 0) + { + Zs[1] = z_coords[1]; + } + else + { + Zs[1] = PLAYER_MAX_Z; + } + + if(z_coords[2] > 0) + { + Zs[2] = z_coords[2]; + } + else + { + Zs[2] = PLAYER_MIN_Z; + } + + set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder)); + le = set->levelorder + set->numlevels - 1; + memset(le, 0, sizeof(*le)); + if(branch_name[0]) + { + le->branchname = NAME(branch_name); + } + le->filename = NAME(filename); + le->z_coords[0] = Zs[0]; + le->z_coords[1] = Zs[1]; + le->z_coords[2] = Zs[2]; + + return le; +} + + + +// Add a scene to the level order +s_level_entry *add_scene(char *filename, s_set_entry *set) +{ + s_level_entry *le = NULL; + + set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder)); + le = set->levelorder + set->numlevels - 1; + memset(le, 0, sizeof(*le)); + if(branch_name[0]) + { + le->branchname = NAME(branch_name); + } + le->filename = NAME(filename); + le->type = LE_TYPE_CUT_SCENE; + return le; +} + +// Add a select screen file to the level order +s_level_entry *add_select(char *filename, s_set_entry *set) +{ + s_level_entry *le = NULL; + + set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder)); + le = set->levelorder + set->numlevels - 1; + memset(le, 0, sizeof(*le)); + if(branch_name[0]) + { + le->branchname = NAME(branch_name); + } + le->filename = NAME(filename); + le->type = LE_TYPE_SELECT_SCREEN; + return le; +} + +s_level_entry *add_skipselect(ArgList arglist, s_set_entry *set) +{ + s_level_entry *le = NULL; + char *arg; + int i = 0; + + set->levelorder = realloc(set->levelorder, (++set->numlevels) * sizeof(*set->levelorder)); + le = set->levelorder + set->numlevels - 1; + memset(le, 0, sizeof(*le)); + if(branch_name[0]) + { + le->branchname = NAME(branch_name); + } + + if(arglist.count == 1) + { + le->noselect = 1; + } + else + { + for(i = 0; i < MAX_PLAYERS; i++) + { + if((arg = GET_ARG(i + 1))[0]) + { + le->skipselect[i] = NAME(arg); + } + } + } + + le->type = LE_TYPE_SKIP_SELECT; + return le; +} + +s_level_entry *load_skipselect(char *buf, s_set_entry *set) +{ + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1]; + + ParseArgs(&arglist, buf, argbuf); + + return add_skipselect(arglist, set); +} + +void save_skipselect(char *buf, char skipselect[MAX_PLAYERS][MAX_NAME_LEN]) +{ + int i = 0; + + strcpy(buf,"skipselect"); + + for (i = 0; i < MAX_PLAYERS; i++) + { + strcat(buf," "); strcat(buf,skipselect[i]); + } + + return; +} + +static void _readbarstatus(char *buf, s_barstatus *pstatus) +{ + char *value; + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + + ParseArgs(&arglist, buf, argbuf); + if((value = GET_ARG(1))[0]) + { + pstatus->size.x = atoi(value); + } + else + { + return; + } + if((value = GET_ARG(2))[0]) + { + pstatus->size.y = atoi(value); + } + else + { + return; + } + if((value = GET_ARG(3))[0]) + { + if (atoi(value)) + { + pstatus->config_flags |= STATUS_CONFIG_BORDER_DISABLE; + } + } + else + { + return; + } + if((value = GET_ARG(4))[0]) + { + if (atoi(value)) + { + pstatus->config_flags |= STATUS_CONFIG_GRAPH_RATIO; + } + } + else + { + return; + } + if((value = GET_ARG(5))[0]) + { + if (atoi(value)) + { + pstatus->config_flags |= STATUS_CONFIG_GRAPH_VERTICAL; + } + } + else + { + return; + } + if((value = GET_ARG(6))[0]) + { + pstatus->borderlayer = atoi(value); + } + else + { + return; + } + if((value = GET_ARG(7))[0]) + { + pstatus->shadowlayer = atoi(value); + } + else + { + return; + } + if((value = GET_ARG(8))[0]) + { + pstatus->barlayer = atoi(value); + } + else + { + return; + } + if((value = GET_ARG(9))[0]) + { + pstatus->backlayer = atoi(value); + } + else + { + return; + } +} + +s_set_entry *add_set() +{ + s_set_entry *set = NULL; + ++num_difficulties; + if(levelsets) + { + levelsets = realloc(levelsets, sizeof(*levelsets) * num_difficulties); + } + else + { + levelsets = calloc(1, sizeof(*levelsets)); + } + set = levelsets + num_difficulties - 1; + memset(set, 0, sizeof(*set)); + set->maxplayers = defaultmaxplayers; + return set; +} + +// Load list of levels +void load_levelorder() +{ + const char *defaulterr = "Error in level order: a set must be specified."; +#define CHKDEF if(!set) { errormessage = (char*) defaulterr; goto lCleanup; } + char filename[MAX_BUFFER_LEN] = ""; + int i = 0, j = 0, err = 0; + char *buf; + size_t size; + int pos; + s_set_entry *set = NULL; + s_level_entry *le = NULL; + char *command; + char *arg; + char *errormessage = NULL; + int plifeUsed[2] = {0, 0}; + int elifeUsed[2] = {0, 0}; + int piconUsed[2] = {0, 0}; + int piconwUsed[2] = {0, 0}; + int eiconUsed[4] = {0, 0, 0, 0}; + int pmpUsed[4] = {0, 0, 0, 0}; + int plifeXused[4] = {0, 0, 0, 0}; // 4-7-2006 New custimizable variable for players 'x' + int plifeNused[4] = {0, 0, 0, 0}; // 4-7-2006 New custimizable variable for players 'lives' + int enameused[4] = {0, 0, 0, 0}; // 4-7-2006 New custimizable variable for enemy names + int pnameJused[4] = {0, 0, 0, 0}; // 1-8-2006 New custimizable variable for players name Select Hero + int pscoreUsed[4] = {0, 0, 0, 0}; // 1-8-2006 New custimizable variable for players name Select Hero + + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + levelOrderCommands cmd; + int line = 0; + + unload_levelorder(); + + if(custLevels != NULL) + { + strcpy(filename, "data/"); + strcat(filename, custLevels); + } + else + { + strcpy(filename, "data/levels.txt"); + } + + // Read file + + if(buffer_pakfile(filename, &buf, &size) != 1) + { + borShutdown(1, "Error loading level list from %s", filename); + } + + // Now interpret the contents of buf line by line + pos = 0; + + // Custom lifebar/timebox/icon positioning and size + picon[0][0] = piconw[0][0] = picon[2][0] = piconw[2][0] = eicon[0][0] = eicon[2][0] = 2; + picon[1][0] = piconw[1][0] = picon[3][0] = piconw[3][0] = eicon[1][0] = eicon[3][0] = 2 + P2_STATS_DIST; + picon[0][1] = piconw[0][1] = picon[1][1] = piconw[1][1] = 2; + picon[2][1] = piconw[2][1] = picon[3][1] = piconw[3][1] = 202; + plife[0][0] = pmp[0][0] = plife[2][0] = pmp[2][0] = elife[0][0] = elife[2][0] = 20; + plife[1][0] = pmp[1][0] = plife[3][0] = pmp[3][0] = elife[1][0] = elife[3][0] = 20 + P2_STATS_DIST; + plife[0][1] = plife[1][1] = 10; + plife[2][1] = plife[3][1] = 210; + pmp[0][1] = pmp[1][1] = 18; + pmp[2][1] = pmp[3][1] = 218; + + memset(psmenu, 0, sizeof(psmenu)); + + eicon[0][1] = eicon[1][1] = 19; + eicon[2][1] = eicon[3][1] = 220; + elife[0][1] = elife[1][1] = 27; + elife[2][1] = elife[3][1] = 227; + + timeloc[0] = 149; + timeloc[1] = 4; + timeloc[2] = 21; + timeloc[3] = 20; + timeloc[4] = 0; + + lbarstatus.size.x = mpbarstatus.size.x = 100; + lbarstatus.size.y = 5; + mpbarstatus.size.y = 3; + lbarstatus.config_flags = STATUS_CONFIG_DEFAULT; + mpbarstatus.config_flags = STATUS_CONFIG_DEFAULT; + + // Show Complete Default Values + scomplete[0] = 75; + scomplete[1] = 60; + scomplete[2] = 0; + scomplete[3] = 0; + scomplete[4] = 0; + scomplete[5] = 0; + + // Show Complete Y Values + cbonus[0] = lbonus[0] = rbonus[0] = tscore[0] = 10; + cbonus[1] = cbonus[3] = cbonus[5] = cbonus[7] = cbonus[9] = 100; + lbonus[1] = lbonus[3] = lbonus[5] = lbonus[7] = lbonus[9] = 120; + rbonus[1] = rbonus[3] = rbonus[5] = rbonus[7] = rbonus[9] = 140; + tscore[1] = tscore[3] = tscore[5] = tscore[7] = tscore[9] = 160; + + // Show Complete X Values + cbonus[2] = lbonus[2] = rbonus[2] = tscore[2] = 100; + cbonus[4] = lbonus[4] = rbonus[4] = tscore[4] = 155; + cbonus[6] = lbonus[6] = rbonus[6] = tscore[6] = 210; + cbonus[8] = lbonus[8] = rbonus[8] = tscore[8] = 265; + + while(pos < size) + { + line++; + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + cmd = getLevelOrderCommand(levelordercmdlist, command); + switch(cmd) + { + case CMD_LEVELORDER_BLENDFX: + for(i = 0; i < MAX_BLENDINGS; i++) + { + if(GET_INT_ARG(i + 1)) + { + blendfx[i] = 1; + } + else + { + blendfx[i] = 0; + } + } + blendfx_is_set = 1; + break; + case CMD_LEVELORDER_SET: + set = add_set(); + set->name = NAME(GET_ARG(1)); + set->ifcomplete = 0; + set->saveflag = 1; // default to 1, so the level can be saved + branch_name[0] = 0; + le = NULL; + break; + case CMD_LEVELORDER_IFCOMPLETE: + CHKDEF; + set->ifcomplete = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_SKIPSELECT: + CHKDEF; + le = add_skipselect(arglist, set); + break; + case CMD_LEVELORDER_FILE: + CHKDEF; + le = add_level(GET_ARG(1), set); + break; + case CMD_LEVELORDER_SCENE: + CHKDEF; + le = add_scene(GET_ARG(1), set); + break; + case CMD_LEVELORDER_SELECT: + CHKDEF; + le = add_select(GET_ARG(1), set); + break; + case CMD_LEVELORDER_NEXT: + CHKDEF; + // Set 'gonext' flag of last loaded level + if(le) + { + le->gonext = 1; + } + break; + case CMD_LEVELORDER_END: + CHKDEF; + // Set endgame flag of last loaded level + if(le) + { + le->gonext = 2; + } + break; + case CMD_LEVELORDER_LIVES: + // 7-1-2005 credits/lives/singleplayer start here + // used to read the new # of lives/credits from the levels.txt + CHKDEF; + set->lives = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_DISABLEHOF: + CHKDEF; + set->noshowhof = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_DISABLEGAMEOVER: + CHKDEF; + set->noshowgameover = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_CANSAVE: + // 07-12-31 + // 0 this set can't be saved + // 1 save level only + // 2 save player info and level, can't choose player in select menu + CHKDEF; + set->saveflag = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_Z: + // 2-10-05 adjust the walkable coordinates + CHKDEF; + z_coords[0] = GET_INT_ARG(1); + z_coords[1] = GET_INT_ARG(2); + z_coords[2] = GET_INT_ARG(3); + break; + case CMD_LEVELORDER_BRANCH: + // 2007-2-22 level branch name + CHKDEF; + strncpy(branch_name, GET_ARG(1), MAX_NAME_LEN); + break; + case CMD_LEVELORDER_P1LIFE: + case CMD_LEVELORDER_P2LIFE: + case CMD_LEVELORDER_P3LIFE: + case CMD_LEVELORDER_P4LIFE: + switch(cmd) + { + case CMD_LEVELORDER_P1LIFE: + i = 0; + break; + case CMD_LEVELORDER_P2LIFE: + i = 1; + break; + case CMD_LEVELORDER_P3LIFE: + i = 2; + plifeUsed[0] = 1; + break; + case CMD_LEVELORDER_P4LIFE: + i = 3; + plifeUsed[1] = 1; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + plife[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + plife[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_P1MP: + case CMD_LEVELORDER_P2MP: + case CMD_LEVELORDER_P3MP: + case CMD_LEVELORDER_P4MP: + switch(cmd) + { + case CMD_LEVELORDER_P1MP: + i = 0; + break; + case CMD_LEVELORDER_P2MP: + i = 1; + break; + case CMD_LEVELORDER_P3MP: + i = 2; + break; + case CMD_LEVELORDER_P4MP: + i = 3; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + pmp[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + pmp[i][1] = atoi(arg); + } + pmpUsed[i] = 1; + break; + case CMD_LEVELORDER_P1LIFEX: + case CMD_LEVELORDER_P2LIFEX: + case CMD_LEVELORDER_P3LIFEX: + case CMD_LEVELORDER_P4LIFEX: + switch(cmd) + { + case CMD_LEVELORDER_P1LIFEX: + j = 0; + break; + case CMD_LEVELORDER_P2LIFEX: + j = 1; + break; + case CMD_LEVELORDER_P3LIFEX: + j = 2; + break; + case CMD_LEVELORDER_P4LIFEX: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 3; i++) + if((arg = GET_ARG(i + 1))[0]) + { + plifeX[j][i] = atoi(arg); + } + plifeXused[j] = 1; + break; + case CMD_LEVELORDER_P1LIFEN: + case CMD_LEVELORDER_P2LIFEN: + case CMD_LEVELORDER_P3LIFEN: + case CMD_LEVELORDER_P4LIFEN: + switch(cmd) + { + case CMD_LEVELORDER_P1LIFEN: + j = 0; + break; + case CMD_LEVELORDER_P2LIFEN: + j = 1; + break; + case CMD_LEVELORDER_P3LIFEN: + j = 2; + break; + case CMD_LEVELORDER_P4LIFEN: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 3; i++) + if((arg = GET_ARG(i + 1))[0]) + { + plifeN[j][i] = atoi(arg); + } + plifeNused[j] = 1; + break; + case CMD_LEVELORDER_E1LIFE: + case CMD_LEVELORDER_E2LIFE: + case CMD_LEVELORDER_E3LIFE: + case CMD_LEVELORDER_E4LIFE: + switch(cmd) + { + case CMD_LEVELORDER_E1LIFE: + i = 0; + break; + case CMD_LEVELORDER_E2LIFE: + i = 1; + break; + case CMD_LEVELORDER_E3LIFE: + i = 2; + elifeUsed[0] = 1; + break; + case CMD_LEVELORDER_E4LIFE: + i = 3; + elifeUsed[1] = 1; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + elife[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + elife[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_P1ICON: + case CMD_LEVELORDER_P2ICON: + case CMD_LEVELORDER_P3ICON: + case CMD_LEVELORDER_P4ICON: + switch(cmd) + { + case CMD_LEVELORDER_P1ICON: + i = 0; + break; + case CMD_LEVELORDER_P2ICON: + i = 1; + break; + case CMD_LEVELORDER_P3ICON: + i = 2; + piconUsed[0] = 1; + break; + case CMD_LEVELORDER_P4ICON: + i = 3; + piconUsed[1] = 1; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + picon[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + picon[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_P1ICONW: + case CMD_LEVELORDER_P2ICONW: + case CMD_LEVELORDER_P3ICONW: + case CMD_LEVELORDER_P4ICONW: + switch(cmd) + { + case CMD_LEVELORDER_P1ICONW: + i = 0; + break; + case CMD_LEVELORDER_P2ICONW: + i = 1; + break; + case CMD_LEVELORDER_P3ICONW: + i = 2; + piconwUsed[0] = 1; + break; + case CMD_LEVELORDER_P4ICONW: + i = 3; + piconwUsed[1] = 1; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + piconw[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + piconw[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_MP1ICON: + case CMD_LEVELORDER_MP2ICON: + case CMD_LEVELORDER_MP3ICON: + case CMD_LEVELORDER_MP4ICON: + switch(cmd) + { + case CMD_LEVELORDER_MP1ICON: + i = 0; + break; + case CMD_LEVELORDER_MP2ICON: + i = 1; + break; + case CMD_LEVELORDER_MP3ICON: + i = 2; + break; + case CMD_LEVELORDER_MP4ICON: + i = 3; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + mpicon[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + mpicon[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_P1NAMEJ: + case CMD_LEVELORDER_P2NAMEJ: + case CMD_LEVELORDER_P3NAMEJ: + case CMD_LEVELORDER_P4NAMEJ: + switch(cmd) + { + case CMD_LEVELORDER_P1NAMEJ: + j = 0; + break; + case CMD_LEVELORDER_P2NAMEJ: + j = 1; + break; + case CMD_LEVELORDER_P3NAMEJ: + j = 2; + break; + case CMD_LEVELORDER_P4NAMEJ: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 7; i++) + if((arg = GET_ARG(i + 1))[0]) + { + pnameJ[j][i] = atoi(arg); + } + pnameJused[j] = 1; + break; + case CMD_LEVELORDER_P1SCORE: + case CMD_LEVELORDER_P2SCORE: + case CMD_LEVELORDER_P3SCORE: + case CMD_LEVELORDER_P4SCORE: + switch(cmd) + { + case CMD_LEVELORDER_P1SCORE: + j = 0; + break; + case CMD_LEVELORDER_P2SCORE: + j = 1; + break; + case CMD_LEVELORDER_P3SCORE: + j = 2; + break; + case CMD_LEVELORDER_P4SCORE: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 7; i++) + if((arg = GET_ARG(i + 1))[0]) + { + pscore[j][i] = atoi(arg); + } + pscoreUsed[j] = 1; + break; + case CMD_LEVELORDER_P1SHOOT: + case CMD_LEVELORDER_P2SHOOT: + case CMD_LEVELORDER_P3SHOOT: + case CMD_LEVELORDER_P4SHOOT: + switch(cmd) + { + case CMD_LEVELORDER_P1SHOOT: + j = 0; + break; + case CMD_LEVELORDER_P2SHOOT: + j = 1; + break; + case CMD_LEVELORDER_P3SHOOT: + j = 2; + break; + case CMD_LEVELORDER_P4SHOOT: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 3; i++) + if((arg = GET_ARG(i + 1))[0]) + { + pshoot[j][i] = atoi(arg); + } + break; + case CMD_LEVELORDER_P1RUSH: + case CMD_LEVELORDER_P2RUSH: + case CMD_LEVELORDER_P3RUSH: + case CMD_LEVELORDER_P4RUSH: + switch(cmd) + { + case CMD_LEVELORDER_P1RUSH: + j = 0; + break; + case CMD_LEVELORDER_P2RUSH: + j = 1; + break; + case CMD_LEVELORDER_P3RUSH: + j = 2; + break; + case CMD_LEVELORDER_P4RUSH: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 8; i++) + if((arg = GET_ARG(i + 1))[0]) + { + prush[j][i] = atoi(arg); + } + break; + case CMD_LEVELORDER_E1ICON: + case CMD_LEVELORDER_E2ICON: + case CMD_LEVELORDER_E3ICON: + case CMD_LEVELORDER_E4ICON: + switch(cmd) + { + case CMD_LEVELORDER_E1ICON: + i = 0; + break; + case CMD_LEVELORDER_E2ICON: + i = 1; + break; + case CMD_LEVELORDER_E3ICON: + i = 2; + eiconUsed[0] = 1; + break; + case CMD_LEVELORDER_E4ICON: + i = 3; + eiconUsed[1] = 1; + break; + default: + assert(0); + } + if((arg = GET_ARG(1))[0]) + { + eicon[i][0] = atoi(arg); + } + if((arg = GET_ARG(2))[0]) + { + eicon[i][1] = atoi(arg); + } + break; + case CMD_LEVELORDER_E1NAME: + case CMD_LEVELORDER_E2NAME: + case CMD_LEVELORDER_E3NAME: + case CMD_LEVELORDER_E4NAME: + switch(cmd) + { + case CMD_LEVELORDER_E1NAME: + j = 0; + break; + case CMD_LEVELORDER_E2NAME: + j = 1; + break; + case CMD_LEVELORDER_E3NAME: + j = 2; + break; + case CMD_LEVELORDER_E4NAME: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < 3; i++) + if((arg = GET_ARG(i + 1))[0]) + { + ename[j][i] = atoi(arg); + } + enameused[j] = 1; + break; + case CMD_LEVELORDER_P1SMENU: + case CMD_LEVELORDER_P2SMENU: + case CMD_LEVELORDER_P3SMENU: + case CMD_LEVELORDER_P4SMENU: + switch(cmd) + { + case CMD_LEVELORDER_P1SMENU: + j = 0; + break; + case CMD_LEVELORDER_P2SMENU: + j = 1; + break; + case CMD_LEVELORDER_P3SMENU: + j = 2; + break; + case CMD_LEVELORDER_P4SMENU: + j = 3; + break; + default: + assert(0); + } + for(i = 0; i < MAX_PLAYERS; i++) + if((arg = GET_ARG(i + 1))[0]) + { + psmenu[j][i] = atoi(arg); + } + break; + case CMD_LEVELORDER_TIMEICON: + strncpy(timeicon_path, GET_ARG(1), 127); + timeicon = loadsprite(timeicon_path, 0, 0, pixelformat); + if((arg = GET_ARG(2))[0]) + { + timeicon_offsets[0] = atoi(arg); + } + if((arg = GET_ARG(3))[0]) + { + timeicon_offsets[1] = atoi(arg); + } + break; + case CMD_LEVELORDER_BGICON: + strncpy(bgicon_path, GET_ARG(1), 127); + bgicon = loadsprite(bgicon_path, 0, 0, pixelformat); + if((arg = GET_ARG(2))[0]) + { + bgicon_offsets[0] = atoi(arg); + } + if((arg = GET_ARG(3))[0]) + { + bgicon_offsets[1] = atoi(arg); + } + if((arg = GET_ARG(4))[0]) + { + bgicon_offsets[2] = atoi(arg); + } + else + { + bgicon_offsets[2] = HUD_Z / 2; + } + break; + case CMD_LEVELORDER_OLICON: + strncpy(olicon_path, GET_ARG(1), 127); + olicon = loadsprite(olicon_path, 0, 0, pixelformat); + if((arg = GET_ARG(2))[0]) + { + olicon_offsets[0] = atoi(arg); + } + if((arg = GET_ARG(3))[0]) + { + olicon_offsets[1] = atoi(arg); + } + if((arg = GET_ARG(4))[0]) + { + olicon_offsets[2] = atoi(arg); + } + else + { + olicon_offsets[2] = HUD_Z * 3; + } + break; + case CMD_LEVELORDER_TIMELOC: + for(i = 0; i < 6; i++) + if((arg = GET_ARG(i + 1))[0]) + { + timeloc[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_PAUSEOFFSET: + for(i = 0; i < 7; i++) + if((arg = GET_ARG(i + 1))[0]) + { + pauseoffset[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_LBARSIZE: + _readbarstatus(buf + pos, &lbarstatus); + break; + case CMD_LEVELORDER_OLBARSIZE: + _readbarstatus(buf + pos, &olbarstatus); + break; + case CMD_LEVELORDER_MPBARSIZE: + _readbarstatus(buf + pos, &mpbarstatus); + break; + case CMD_LEVELORDER_LBARTEXT: + for(i = 0; i < 4; i++) + if((arg = GET_ARG(i + 1))[0]) + { + lbartext[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_MPBARTEXT: + for(i = 0; i < 4; i++) + if((arg = GET_ARG(i + 1))[0]) + { + mpbartext[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_SHOWCOMPLETE: + for(i = 0; i < 6; i++) + if((arg = GET_ARG(i + 1))[0]) + { + scomplete[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_CLEARBONUS: + for(i = 0; i < 10; i++) + if((arg = GET_ARG(i + 1))[0]) + { + cbonus[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_RUSHBONUS: + for(i = 0; i < 10; i++) + if((arg = GET_ARG(i + 1))[0]) + { + rbonus[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_LIFEBONUS: + for(i = 0; i < 10; i++) + if((arg = GET_ARG(i + 1))[0]) + { + lbonus[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_SCBONUSES: + for(i = 0; i < 4; i++) + if((arg = GET_ARG(i + 1))[0]) + { + scbonuses[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_TOTALSCORE: + for(i = 0; i < 10; i++) + if((arg = GET_ARG(i + 1))[0]) + { + tscore[i] = atoi(arg); + } + break; + case CMD_LEVELORDER_MUSICOVERLAP: + CHKDEF; + set->musicoverlap = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_SHOWRUSHBONUS: + showrushbonus = 1; + break; + case CMD_LEVELORDER_NOSLOWFX: + noslowfx = 1; + break; + case CMD_LEVELORDER_EQUALAIRPAUSE: + equalairpause = 1; + break; + case CMD_LEVELORDER_HISCOREBG: + hiscorebg = 1; + break; + case CMD_LEVELORDER_COMPLETEBG: + completebg = 1; + break; + case CMD_LEVELORDER_LOADINGBG: + errormessage = fill_s_loadingbar(&loadingbg[0], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8)); + if(errormessage) + { + goto lCleanup; + } + break; + case CMD_LEVELORDER_LOADINGBG2: + errormessage = fill_s_loadingbar(&loadingbg[1], GET_INT_ARG(1), GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8)); + if(errormessage) + { + goto lCleanup; + } + break; + case CMD_LEVELORDER_LOADINGMUSIC: + loadingmusic = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_UNLOCKBG: + unlockbg = 1; + break; + case CMD_LEVELORDER_NOSHARE: + noshare = 1; + break; + case CMD_LEVELORDER_CUSTFADE: + //8-2-2005 custom fade + CHKDEF; + set->custfade = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_CONTINUESCORE: + //8-2-2005 custom fade end + //continuescore + CHKDEF; + set->continuescore = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_CREDITS: + CHKDEF; + set->credits = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_TYPEMP: + //typemp for change for mp restored by time (0) to by enemys (1) or no restore (2) by tails + CHKDEF; + set->typemp = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_SINGLE: + if(set) + { + set->maxplayers = 1; + } + else + { + defaultmaxplayers = 1; + } + break; + case CMD_LEVELORDER_MAXPLAYERS: + if(set) + { + set->maxplayers = GET_INT_ARG(1); + } + else + { + defaultmaxplayers = GET_INT_ARG(1); + } + break; + case CMD_LEVELORDER_NOSAME: + CHKDEF; + set->nosame |= GET_INT_ARG(1) != 0; + set->nosame |= GET_INT_ARG(2) != 0 ? 2 : 0; + break; + case CMD_LEVELORDER_RUSH: + rush[0] = GET_INT_ARG(1); + rush[1] = GET_INT_ARG(2); + strncpy(rush_names[0], GET_ARG(3), MAX_NAME_LEN - 1); + rush[2] = GET_INT_ARG(4); + rush[3] = GET_INT_ARG(5); + strncpy(rush_names[1], GET_ARG(6), MAX_NAME_LEN - 1); + rush[4] = GET_INT_ARG(7); + rush[5] = GET_INT_ARG(8); + break; + case CMD_LEVELORDER_MAXWALLHEIGHT: + MAX_WALL_HEIGHT = GET_INT_ARG(1); + if(MAX_WALL_HEIGHT < 0) + { + MAX_WALL_HEIGHT = 1000; + } + break; + case CMD_LEVELORDER_SCOREFORMAT: + scoreformat = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_GRAVITY: + default_level_gravity = GET_FLOAT_ARG(1); + default_level_maxfallspeed = GET_FLOAT_ARG(2); + default_level_maxtossspeed = GET_FLOAT_ARG(3); + break; + case CMD_LEVELORDER_SKIPTOSET: + skiptoset = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_NOSHOWCOMPLETE: + CHKDEF; + set->noshowcomplete = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_SPAWNOVERRIDE: + spawnoverride = GET_INT_ARG(1); + break; + case CMD_LEVELORDER_MAXENTITIES: + maxentities = GET_INT_ARG(1); + break; + default: + if (command && command[0]) + { + if (err <= 0) printf("\n"); + ++err; + printf("Command '%s' not understood in level order!\n", command); + } + } + + // Go to next line + pos += getNewLineStart(buf + pos); + } + +#undef CHKDEF + + // Variables without defaults will be auto populated. + if(olbarstatus.size.x == 0) + { + olbarstatus = lbarstatus; + } + + if(!plifeUsed[0]) + { + plife[2][0] = plife[0][0]; + plife[2][1] = plife[2][1] + (plife[0][1] - 10); + } + if(!plifeUsed[1]) + { + plife[3][0] = plife[1][0]; + plife[3][1] = plife[3][1] + (plife[1][1] - 10); + } + + if(!elifeUsed[0]) + { + elife[2][0] = elife[0][0]; + elife[2][1] = elife[2][1] + (elife[0][1] - 27); + } + if(!elifeUsed[1]) + { + elife[3][0] = elife[1][0]; + elife[3][1] = elife[3][1] + (elife[1][1] - 27); + } + + if(!piconUsed[0]) + { + picon[2][0] = picon[0][0]; + picon[2][1] = picon[2][1] + (picon[0][1] - 2); + } + if(!piconUsed[1]) + { + picon[3][0] = picon[1][0]; + picon[3][1] = picon[3][1] + (picon[1][1] - 2); + } + + if(!piconwUsed[0]) + { + piconw[2][0] = piconw[0][0]; + piconw[2][1] = piconw[2][1] + (piconw[0][1] - 2); + } + if(!piconwUsed[1]) + { + piconw[3][0] = piconw[1][0]; + piconw[3][1] = piconw[3][1] + (piconw[1][1] - 2); + } + + if(!eiconUsed[0]) + { + eicon[2][0] = eicon[0][0]; + eicon[2][1] = eicon[2][1] + (eicon[0][1] - 19); + } + if(!eiconUsed[1]) + { + eicon[3][0] = eicon[1][0]; + eicon[3][1] = eicon[3][1] + (eicon[1][1] - 19); + } + + if(!pmpUsed[0]) + { + pmp[0][0] = plife[0][0]; + pmp[0][1] = plife[0][1] + 8; + } + if(!pmpUsed[1]) + { + pmp[1][0] = plife[1][0]; + pmp[1][1] = plife[1][1] + 8; + } + if(!pmpUsed[2]) + { + pmp[2][0] = pmp[0][0]; + pmp[2][1] = pmp[2][1] + (pmp[0][1] - 18); + } + if(!pmpUsed[3]) + { + pmp[3][0] = pmp[1][0]; + pmp[3][1] = pmp[1][1] + (pmp[1][1] - 18); + } + + if(!plifeXused[0]) + { + plifeX[0][0] = plife[0][0] + lbarstatus.size.x + 4; + plifeX[0][1] = picon[0][1] + 7; + } + if(!plifeXused[1]) + { + plifeX[1][0] = plife[1][0] + lbarstatus.size.x + 4; + plifeX[1][1] = picon[1][1] + 7; + } + if(!plifeXused[2]) + { + plifeX[2][0] = plife[2][0] + lbarstatus.size.x + 4; + plifeX[2][1] = picon[2][1] + 7; + } + if(!plifeXused[3]) + { + plifeX[3][0] = plife[3][0] + lbarstatus.size.x + 4; + plifeX[3][1] = picon[3][1] + 7; + } + for(i = 0; i < MAX_PLAYERS; i++) if(plifeX[i][2] == -1) + { + plifeX[i][2] = 0; + } + + if(!plifeNused[0]) + { + plifeN[0][0] = plife[0][0] + lbarstatus.size.x + 11; + plifeN[0][1] = picon[0][1]; + } + if(!plifeNused[1]) + { + plifeN[1][0] = plife[1][0] + lbarstatus.size.x + 11; + plifeN[1][1] = picon[1][1]; + } + if(!plifeNused[2]) + { + plifeN[2][0] = plifeN[0][0]; + plifeN[2][1] = picon[2][1]; + } + if(!plifeNused[3]) + { + plifeN[3][0] = plifeN[1][0]; + plifeN[3][1] = picon[3][1]; + } + for(i = 0; i < MAX_PLAYERS; i++) if(plifeN[i][2] == -1) + { + plifeN[i][2] = 3; + } + + if(!pnameJused[0]) + { + pnameJ[0][2] = pnameJ[0][4] = pnameJ[0][0] = plife[0][0] + 1; + pnameJ[0][5] = pnameJ[0][1] = picon[0][1]; + pnameJ[0][3] = 10 + pnameJ[0][5]; + } + if(!pnameJused[1]) + { + pnameJ[1][2] = pnameJ[1][4] = pnameJ[1][0] = plife[1][0] + 1; + pnameJ[1][5] = pnameJ[1][1] = picon[1][1]; + pnameJ[1][3] = 10 + pnameJ[1][5]; + } + if(!pnameJused[2]) + { + pnameJ[2][2] = pnameJ[2][4] = pnameJ[2][0] = plife[2][0] + 1; + pnameJ[2][5] = pnameJ[2][1] = picon[2][1]; + pnameJ[2][3] = 10 + pnameJ[2][5]; + } + if(!pnameJused[3]) + { + pnameJ[3][2] = pnameJ[3][4] = pnameJ[3][0] = plife[3][0] + 1; + pnameJ[3][5] = pnameJ[3][1] = picon[3][1]; + pnameJ[3][3] = 10 + pnameJ[3][5]; + } + for(i = 0; i < MAX_PLAYERS; i++) if(pnameJ[i][6] == -1) + { + pnameJ[i][6] = 0; + } + + if(!pscoreUsed[0]) + { + pscore[0][0] = plife[0][0] + 1; + pscore[0][1] = picon[0][1]; + } + if(!pscoreUsed[1]) + { + pscore[1][0] = plife[1][0] + 1; + pscore[1][1] = picon[1][1]; + } + if(!pscoreUsed[2]) + { + pscore[2][0] = plife[2][0] + 1; + pscore[2][1] = picon[2][1]; + } + if(!pscoreUsed[3]) + { + pscore[3][0] = plife[3][0] + 1; + pscore[3][1] = picon[3][1]; + } + for(i = 0; i < MAX_PLAYERS; i++) if(pscore[i][6] == -1) + { + pscore[i][6] = 0; + } + + if(!enameused[0]) + { + ename[0][0] = elife[0][0] + 1; + ename[0][1] = eicon[0][1]; + } + if(!enameused[1]) + { + ename[1][0] = elife[1][0] + 1; + ename[1][1] = eicon[1][1]; + } + if(!enameused[2]) + { + ename[2][0] = ename[0][0]; + ename[2][1] = eicon[2][1]; + } + if(!enameused[3]) + { + ename[3][0] = ename[1][0]; + ename[3][1] = eicon[3][1]; + } + for(i = 0; i < MAX_PLAYERS; i++) if(ename[i][2] == -1) + { + ename[i][2] = 0; + } + + branch_name[0] = 0; //clear up branch name, so we can use it in game + + for(i = 0; i < MAX_PLAYERS; i++) if(pshoot[i][2] == -1) + { + pshoot[i][2] = 2; + } + if(timeloc[5] == -1) + { + timeloc[5] = 3; + } + + if(!set) + { + errormessage = "No levels were loaded!"; + } + + //assume old mods have same maxplayers for all sets + else if(!psmenu[0][0] && !psmenu[0][1]) + { + for(i = 0; i < set->maxplayers; i++) + { + psmenu[i][0] = (set->maxplayers > 2) ? ((111 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) : + (83 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift))); + psmenu[i][1] = 230 + videomodes.vShift; + psmenu[i][2] = (set->maxplayers > 2) ? ((95 - (set->maxplayers * 14)) + ((i * (320 - (166 / set->maxplayers)) / set->maxplayers) + videomodes.hShift)) : + (67 + (videomodes.hShift / 2) + (i * (155 + videomodes.hShift))); + psmenu[i][3] = 225 + videomodes.vShift; + } + } + +lCleanup: + + if(buf) + { + free(buf); + } + + if(!savelevel || savelevel_count != (size_t)num_difficulties) + { + clear_saved_allowselect_arguments(); + free(savelevel_allowselect_args); + free(savelevel); + + savelevel_count = (size_t)num_difficulties; + savelevel = calloc(savelevel_count, sizeof(*savelevel)); + savelevel_allowselect_args = calloc( + savelevel_count, + sizeof(*savelevel_allowselect_args) + ); + } + else if(!savelevel_allowselect_args) + { + savelevel_allowselect_args = calloc( + savelevel_count, + sizeof(*savelevel_allowselect_args) + ); + } + + if(errormessage) + { + borShutdown(1, "load_levelorder ERROR in %s at %d, msg: %s\n", filename, line, errormessage); + } +} + + +void free_level(s_level *lv) +{ + int i; + + if(!lv) + { + return; + } + + //offload layers + for(i = 1; i < lv->numlayers; i++) + { + if(lv->layers[i].gfx.handle && lv->layers[i].gfx.handle != background) + { + if(lv->layers[i].gfx.sprite->magic == sprite_magic && + lv->layers[i].gfx.sprite->mask != NULL) + { + free(lv->layers[i].gfx.sprite->mask); + } + free(lv->layers[i].gfx.handle); + lv->layers[i].gfx.handle = NULL; + } + } + + //offload textobjs + for(i = 0; i < lv->numtextobjs; i++) + { + if(lv->textobjs[i].text) + { + free(lv->textobjs[i].text); + lv->textobjs[i].text = NULL; + } + } + + //offload basemaps + for(i = 0; i < lv->numbasemaps; i++) + { + if(lv->basemaps[i].map) + { + free(lv->basemaps[i].map); + } + } + + //offload scripts + Script_Clear(&(lv->update_script), 2); + Script_Clear(&(lv->updated_script), 2); + Script_Clear(&(lv->update_logic_script), 2); + Script_Clear(&(lv->updated_logic_script), 2); + Script_Clear(&(lv->key_script), 2); + Script_Clear(&(lv->level_script), 2); + Script_Clear(&(lv->endlevel_script), 2); + + for(i = 0; i < lv->numspawns; i++) + { + Script_Clear(&(lv->spawnpoints[i].spawnscript), 2); + } + + if(lv->spawnpoints) + { + free(lv->spawnpoints); + } + if(lv->layers) + { + free(lv->layers); + } + if(lv->layersref) + { + free(lv->layersref); + } + if(lv->panels) + { + free(lv->panels); + } + if(lv->frontpanels) + { + free(lv->frontpanels); + } + if(lv->bglayers) + { + free(lv->bglayers); + } + if(lv->fglayers) + { + free(lv->fglayers); + } + if(lv->genericlayers) + { + free(lv->genericlayers); + } + if(lv->waters) + { + free(lv->waters); + } + if(lv->textobjs) + { + free(lv->textobjs); + } + if(lv->holes) + { + free(lv->holes); + } + if(lv->spawn) + { + free(lv->spawn); + } + if(lv->walls) + { + free(lv->walls); + } + if(lv->basemaps) + { + free(lv->basemaps); + } + if(lv->palettes) + { + free(lv->palettes); + } + + free(lv); + lv = NULL; +} + +/* +* Caskey, Damon V. +* 2026-08-09 +* +* Accept model pointer and check if it +* is the model of an active player. If so, +* return true; otherwise, return false. +*/ +static bool model_is_active_player(const s_model *model) { + int i; + + if(!model || !model->name) { + return false; + } + + for(i = 0; i < MAX_PLAYERS; i++) { + if(player[i].lives > 0 + && player[i].name[0] + && stricmp(player[i].name, model->name) == 0) { + return true; + } + } + + return false; +} + + +void unload_level() +{ + s_model *temp; + int i; + + kill_all(); + unload_background(); + + if(level) + { + + level->pos = 0; + level->advancetime = 0; + level->quake = 0; + level->quaketime = 0; + level->waiting = 0; + + printf("Level Unloading: '%s'\n", level->name); + getRamStatus(BYTES); + free(level->name); + level->name = NULL; + free_level(level); + level = NULL; + temp = getFirstModel(); + do + { + if(!temp) + { + break; + } + + // Players carry their model choice into the next level. Keep the + // corresponding model and sprites resident while the player is + // alive, even when the model was loaded with an unload flag. + if(model_is_active_player(temp)) + { + temp = getNextModel(); + continue; + } + + if((temp->unload & 2)) + { + cache_model_sprites(temp, 0); + } + if((temp->unload & 1)) + { + free_model(temp); + temp = getCurrentModel(); + } + else + { + temp = getNextModel(); + } + } + while(temp); + printf("RAM Status:\n"); + getRamStatus(BYTES); + + + } + + advancex = 0; + advancey = 0; + nojoin = 0; + is_total_timeover = 0; + current_spawn = 0; + groupmin = 100; + groupmax = 100; + scrollminz = 0; + scrollmaxz = 0; + scrollminx = 0; + scrollmaxx = 0; + blockade = 0; + level_completed = 0; + level_completed_defeating_boss = 0; + tospeedup = 0; // Reset so it sets to normal speed for the next level + + for (i = 0; i < MAX_PLAYERS; i++){ + reached[i] = false; // TYPE_ENDLEVEL values reset after level completed //4player + } + + showtimeover = 0; + _pause = 0; + endgame = 0; + go_time = 0; + debug_time = 0; + debug_xy_msg.x = -1; + debug_xy_msg.y = -1; + neon_time = 0; + _time = 0; + cameratype = 0; + light.x = 128; + light.y = 64; + gfx_y_offset = gfx_x_offset = gfx_y_offset_adj = 0; // Added so select screen graphics display correctly +} + + +static void addhole(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type) +{ + __realloc(level->holes, level->numholes); + level->holes[level->numholes].x = x; + level->holes[level->numholes].z = z; + level->holes[level->numholes].upperleft = x1; + level->holes[level->numholes].lowerleft = x2; + level->holes[level->numholes].upperright = x3; + level->holes[level->numholes].lowerright = x4; + level->holes[level->numholes].depth = depth; + level->holes[level->numholes].height = alt; + level->holes[level->numholes].type = type; + + level->numholes++; +} + +static void addwall(float x, float z, float x1, float x2, float x3, float x4, float depth, float alt, int type) +{ + __realloc(level->walls, level->numwalls); + level->walls[level->numwalls].x = x; + level->walls[level->numwalls].z = z; + level->walls[level->numwalls].upperleft = x1; + level->walls[level->numwalls].lowerleft = x2; + level->walls[level->numwalls].upperright = x3; + level->walls[level->numwalls].lowerright = x4; + level->walls[level->numwalls].depth = depth; + level->walls[level->numwalls].height = alt; + level->walls[level->numwalls].type = type; + + level->numwalls++; +} + +static void addbasemap(float rx, float rz, float x_size, float z_size, float min_y, float max_y, int x_cont) +{ + __realloc(level->basemaps, level->numbasemaps); + level->numbasemaps++; + generate_basemap(level->numbasemaps-1, rx, rz, x_size, z_size, min_y, max_y, x_cont); +} + +void generate_basemap(int map_index, float rx, float rz, float x_size, float z_size, float min_y, float max_y, int x_cont) { + float x, z; + float delta, y, tmp; + int dir = 0; + + if(map_index >= level->numbasemaps) + { + __reallocto(level->basemaps, level->numbasemaps, map_index + 1); + level->numbasemaps = map_index + 1; + } + + level->basemaps[map_index].position.x = rx; + level->basemaps[map_index].position.z = rz-z_size; + level->basemaps[map_index].size.x = x_size; + level->basemaps[map_index].size.z = z_size; + + if(!level->basemaps[map_index].map) + { + level->basemaps[map_index].map = calloc( 1, (int)(sizeof(*(level->basemaps[map_index].map)) * (x_size+1)*(z_size+1)) ); + } + + if (min_y <= max_y) dir = 1; + else + { + dir = 0; + tmp = min_y; + min_y = max_y; + max_y = tmp; + } + + delta = (max_y - min_y) / ( (x_size <= 0) ? 1 : (x_size-1) ); + + for( x = 0; x < x_size; x++) + { + if ( dir ) + { + if ( x == x_size-1 ) y = max_y; + else y = x*delta + min_y; + } + else + { + y = max_y - (x*delta); + } + + if ( x_cont != 0 ) + { + if ( dir > 0 ) + { + if ( x+rx >= x_cont ) y = max_y; // connect with the wall more smoothly + } + else + { + if ( x+rx <= x_cont ) y = max_y; + } + } + + for ( z = 0; z < z_size; z++) + { + level->basemaps[map_index].map[(int)(x + z*x_size)] = y; + //printf("map[%d] = %f\n",(int)(x + z*x_size),y); + } + //printf("x:%f y:%f delta:%f\n",x,y,delta); + //printf("y: %f\n",y); + } + + return; +} + +void load_level(char *filename) +{ + char *buf = NULL; + size_t size, len, sblen; + ptrdiff_t pos, oldpos; + char *command; + char *value; + char *scriptbuf = NULL; + char string[MAX_BUFFER_LEN] = {""}; + s_spawn_entry next; + s_model *tempmodel, *cached_model; + + int i = 0, j = 0, crlf = 0; + int player_max = MAX_PLAYERS; + char bgPath[MAX_BUFFER_LEN] = {""}, fnbuf[MAX_BUFFER_LEN]; + s_loadingbar bgPosi = {0, 0, {0,0}, {0,0}, 0, 0}; + char musicPath[MAX_BUFFER_LEN] = {""}; + u32 musicOffset = 0; + + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + + ArgList arglist2; + char argbuf2[MAX_ARG_LEN + 1] = ""; + + levelCommands cmd; + levelCommands cmd2; + int line = 0; + char *errormessage = NULL; + char *scriptname = NULL; + Script *tempscript = NULL; + s_drawmethod *dm; + s_layer *bgl; + int (*panels)[3] = NULL; + int *order = NULL; + int panelcount = 0; + int exit_blocked = 0, exit_hole = 0; + char maskPath[MAX_BUFFER_LEN] = {""}; + + unload_level(); + + printf("Level Loading: '%s'\n", filename); + + + + getRamStatus(BYTES); + + if(isLoadingScreenTypeBg(loadingbg[1].set)) + { + if(custBkgrds) + { + strcpy(string, custBkgrds); + strcat(string, "loading2"); + load_background(string); + } + else + { + load_cached_background("data/bgs/loading2"); + } + clearscreen(vscreen); + spriteq_clear(); + standard_palette(1); + } + + if(isLoadingScreenTypeBar(loadingbg[1].set)) + { + lifebar_colors(); + init_colourtable(); + } + + update_loading(&loadingbg[1], -1, 1); // initialize the update screen + + memset(&next, 0, sizeof(next)); + + level = calloc(1, sizeof(*level)); + if(!level) + { + errormessage = "load_level() #1 FATAL: Out of memory!"; + goto lCleanup; + } + len = strlen(filename); + level->name = malloc(len + 1); + + if(!level->name) + { + errormessage = "load_level() #1 FATAL: Out of memory!"; + goto lCleanup; + } + strcpy(level->name, filename); + + // Allocate memory for player spawn - only as much as we need. + player_max = levelsets[current_set].maxplayers; + level->spawn = calloc(player_max, sizeof(*level->spawn)); + + // Default player spawn Y position just above the screen top. + for(i = 0; i < player_max && level->spawn; i++) + { + level->spawn[i].y = videomodes.vRes + 60; + } + + if(buffer_pakfile(filename, &buf, &size) != 1) + { + errormessage = "Unable to load level file!"; + goto lCleanup; + } + + level->settime = 100; // Feb 25, 2005 - Default time limit set to 100 + level->nospecial = 0; // Default set to specials can be used during bonus levels + level->nohurt = DAMAGE_FROM_ENEMY_ON; + level->nohit = DAMAGE_FROM_PLAYER_ON; // Default able to hit the other player + level->setweap = 0; + level->maxtossspeed = default_level_maxtossspeed; + level->maxfallspeed = default_level_maxfallspeed; + level->gravity = default_level_gravity; + level->scrolldir = SCROLL_RIGHT; + level->scrollspeed = 1; + level->cameraxoffset = 0; + level->camerazoffset = 0; + level->boss_slow = BOSS_SLOW_ON; + level->bossescount = 0; + level->numbosses = 0; + blendfx[BLEND_MULTIPLY] = 1; + bgtravelled = 0; + vbgtravelled = 0; + traveltime = 0; + texttime = 0; + nopause = 0; + nofadeout = 0; + noscreenshot = 0; + + panel_width = panel_height = frontpanels_loaded = 0; + + //reset_playable_list(1); + + // Now interpret the contents of buf line by line + pos = 0; + while(pos < size) + { + line++; + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + cmd = getLevelCommand(levelcmdlist, command); + switch(cmd) + { + case CMD_LEVEL_LOADINGBG: + load_background(GET_ARG(1)); + errormessage = fill_s_loadingbar(&bgPosi, GET_INT_ARG(2), GET_INT_ARG(3), GET_INT_ARG(4), GET_INT_ARG(5), GET_INT_ARG(6), GET_INT_ARG(7), GET_INT_ARG(8), GET_INT_ARG(9)); + if (errormessage) + { + goto lCleanup; + } + standard_palette(1); + lifebar_colors(); + init_colourtable(); + update_loading(&bgPosi, -1, 1); // initialize the update screen + break; + case CMD_LEVEL_MUSICFADE: + memset(&next, 0, sizeof(next)); + next.musicfade = GET_FLOAT_ARG(1); + break; + case CMD_LEVEL_MUSIC: + value = GET_ARG(1); + strncpy(string, value, MAX_BUFFER_LEN - 1); + musicOffset = atol(GET_ARG(2)); + if(loadingmusic) + { + music(string, 1, musicOffset); + musicPath[0] = 0; + } + else + { + oldpos = pos; + // Go to next line + pos += getNewLineStart(buf + pos); +#define GET_ARG2(z) arglist2.count > z ? arglist2.args[z] : "" + if(pos < size) + { + ParseArgs(&arglist2, buf + pos, argbuf2); + command = GET_ARG2(0); + cmd2 = getLevelCommand(levelcmdlist, command); + } + else + { + cmd2 = (levelCommands) 0; + } + + if(cmd2 == CMD_LEVEL_AT) + { + if(next.musicfade == 0) + { + memset(&next, 0, sizeof(next)); + } + strncpy(next.music, string, MAX_BUFFER_LEN); + next.musicoffset = musicOffset; + } + else + { + strncpy(musicPath, string, MAX_BUFFER_LEN); + } + pos = oldpos; +#undef GET_ARG2 + } + break; + case CMD_LEVEL_ALLOWSELECT: + load_playable_list(buf + pos); + break; + case CMD_LEVEL_LOAD: +#ifdef DEBUG + printf("load_level: load %s, %s\n", GET_ARG(1), filename); +#endif + tempmodel = findmodel(GET_ARG(1)); + if (!tempmodel) + { + load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2)); + } + else + { + update_model_loadflag(tempmodel, GET_INT_ARG(2)); + } + break; + case CMD_LEVEL_ALPHAMASK: + strncpy(maskPath, GET_ARG(1), MAX_BUFFER_LEN - 1); + break; + case CMD_LEVEL_BACKGROUND: + case CMD_LEVEL_BGLAYER: + case CMD_LEVEL_LAYER: + case CMD_LEVEL_FGLAYER: + __realloc(level->layers, level->numlayers); + bgl = &(level->layers[level->numlayers]); + + if(cmd == CMD_LEVEL_BACKGROUND || cmd == CMD_LEVEL_BGLAYER) + { + i = 0; + bgl->z = MIN_INT; + } + else + { + i = 1; + bgl->z = GET_FLOAT_ARG(2); + if(cmd == CMD_LEVEL_FGLAYER) + { + bgl->z += FRONTPANEL_Z; + } + } + + if(cmd == CMD_LEVEL_BACKGROUND) + { + if(bgPath[0]) + { + errormessage = "Background is already defined!"; + goto lCleanup; + } + value = GET_ARG(1); + strcpy(bgPath, value); + bgl->oldtype = BGT_BACKGROUND; + } + else if(cmd == CMD_LEVEL_BGLAYER) + { + bgl->oldtype = BGT_BGLAYER; + } + else if(cmd == CMD_LEVEL_FGLAYER) + { + bgl->oldtype = BGT_FGLAYER; + } + else if(cmd == CMD_LEVEL_LAYER) + { + bgl->oldtype = BGT_GENERIC; + } + + dm = &(bgl->drawmethod); + *dm = plainmethod; + + bgl->ratio.x = GET_FLOAT_ARG(i + 2); // x ratio + bgl->ratio.z = GET_FLOAT_ARG(i + 3); // z ratio + bgl->offset.x = GET_INT_ARG(i + 4); // x start + bgl->offset.z = GET_INT_ARG(i + 5); // z start + bgl->spacing.x = GET_INT_ARG(i + 6); // x spacing + bgl->spacing.z = GET_INT_ARG(i + 7); // z spacing + dm->xrepeat = GET_INT_ARG(i + 8); // x repeat + dm->yrepeat = GET_INT_ARG(i + 9); // z repeat + + dm->config = GET_INT_ARG(i + 10) ? (dm->config | DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY) : (dm->config & ~DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY); + + + dm->alpha = GET_INT_ARG(i + 11); // alpha + dm->water.watermode = GET_INT_ARG(i + 12); // water + if(dm->water.watermode == WATER_MODE_SHEAR) + { + dm->water.beginsize = GET_FLOAT_ARG(i + 13); // beginsize + dm->water.endsize = GET_FLOAT_ARG(i + 14); // endsize + dm->water.perspective = GET_INT_ARG(i + 15); // perspective + } + else + { + dm->water.amplitude = GET_INT_ARG(i + 13); // amplitude + dm->water.wavelength = GET_FLOAT_ARG(i + 14); // wavelength + dm->water.wavespeed = GET_FLOAT_ARG(i + 15); // waterspeed + } + bgl->bgspeedratio = GET_FLOAT_ARG(i + 16); // moving + bgl->quake = GET_INT_ARG(i + 17); // quake + bgl->neon = GET_INT_ARG(i + 18); // neon + bgl->enabled = 1; // enabled + + if((GET_ARG(i + 2))[0] == 0) + { + bgl->ratio.x = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5); + } + if((GET_ARG(i + 3))[0] == 0) + { + bgl->ratio.z = (cmd == CMD_LEVEL_FGLAYER ? 1.5 : 0.5); + } + + if((GET_ARG(i + 8))[0] == 0) + { + dm->xrepeat = -1; + } + if((GET_ARG(i + 9))[0] == 0) + { + dm->yrepeat = -1; + } + if(cmd == CMD_LEVEL_BACKGROUND && (GET_ARG(i + 16))[0] == 0) + { + bgl->bgspeedratio = 1.0; + } + + if(blendfx_is_set == 0 && dm->alpha) + { + blendfx[dm->alpha - 1] = 1; + } + + if(cmd != CMD_LEVEL_BACKGROUND) + { + load_layer(GET_ARG(1), maskPath, level->numlayers); + } + level->numlayers++; + break; + case CMD_LEVEL_WATER: + __realloc(level->layers, level->numlayers); + bgl = &(level->layers[level->numlayers]); + dm = &(bgl->drawmethod); + *dm = plainmethod; + + bgl->oldtype = BGT_WATER; + bgl->z = MIN_INT + 1; + + bgl->ratio.x = 0.5; // x ratio + bgl->ratio.z = 0.5; // z ratio + bgl->offset.x = 0; // x start + bgl->offset.z = NaN; // z start + bgl->spacing.x = 0; // x spacing + bgl->spacing.z = 0; // z spacing + dm->xrepeat = -1; // x repeat + dm->yrepeat = 1; // z repeat + dm->config &= ~DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY; // transparency + dm->alpha = BLEND_MODE_NONE; // alpha + dm->water.watermode = WATER_MODE_SINE; + dm->water.amplitude = GET_INT_ARG(2); // amplitude + dm->water.wavelength = 40; // wavelength + dm->water.wavespeed = 1.0; // waterspeed + bgl->bgspeedratio = 0; // moving + bgl->enabled = 1; // enabled + + if(dm->water.amplitude < 1) + { + dm->water.amplitude = 1; + } + + load_layer(GET_ARG(1), maskPath, level->numlayers); + level->numlayers++; + break; + case CMD_LEVEL_DIRECTION: + value = GET_ARG(1); + if(stricmp(value, "up") == 0) + { + level->scrolldir = SCROLL_UP; + } + else if(stricmp(value, "down") == 0) + { + level->scrolldir = SCROLL_DOWN; + } + else if(stricmp(value, "left") == 0) + { + level->scrolldir = SCROLL_LEFT; + } + else if(stricmp(value, "both") == 0 || stricmp(value, "rightleft") == 0) + { + level->scrolldir = SCROLL_BOTH; + } + else if(stricmp(value, "leftright") == 0) + { + level->scrolldir = SCROLL_LEFTRIGHT; + } + else if(stricmp(value, "right") == 0) + { + level->scrolldir = SCROLL_RIGHT; + } + else if(stricmp(value, "in") == 0) + { + level->scrolldir = SCROLL_INWARD; + } + else if(stricmp(value, "out") == 0) + { + level->scrolldir = SCROLL_OUTWARD; + } + else if(stricmp(value, "inout") == 0) + { + level->scrolldir = SCROLL_INOUT; + } + else if(stricmp(value, "outin") == 0) + { + level->scrolldir = SCROLL_OUTIN; + } + break; + case CMD_LEVEL_FACING: + level->facing = GET_INT_ARG(1); + break; + + case CMD_LEVEL_ROCK: + level->rocking = GET_INT_ARG(1); + break; + case CMD_LEVEL_BGSPEED: + level->bgspeed = GET_FLOAT_ARG(1); + if(GET_INT_ARG(2)) + { + level->bgspeed *= -1; + } + break; + case CMD_LEVEL_VBGSPEED: + level->vbgspeed = GET_FLOAT_ARG(1); + if(GET_INT_ARG(2)) + { + level->vbgspeed *= -1; + } + break; + case CMD_LEVEL_SCROLLSPEED: + level->scrollspeed = GET_FLOAT_ARG(1); + break; + case CMD_LEVEL_MIRROR: + level->mirror = GET_INT_ARG(1); + break; + case CMD_LEVEL_BOSSMUSIC: + strncpy(level->bossmusic, GET_ARG(1), 255); + level->bossmusic_offset = atol(GET_ARG(2)); + break; + case CMD_LEVEL_NOSAVE: + nosave = GET_INT_ARG(1); + break; + case CMD_LEVEL_NOFADEOUT: + nofadeout = GET_INT_ARG(1); + break; + case CMD_LEVEL_NOPAUSE: + nopause = GET_INT_ARG(1); + break; + case CMD_LEVEL_NOSCREENSHOT: + noscreenshot = GET_INT_ARG(1); + break; + case CMD_LEVEL_SETTIME: + // If settime is found, overwrite the default 100 for time limit + level->settime = GET_INT_ARG(1); + if(level->settime > 100 || level->settime < 0) + { + level->settime = 100; + } + // Feb 25, 2005 - time limit loaded from individual .txt file + break; + case CMD_LEVEL_SETWEAP: + // Specify a weapon for each level + level->setweap = GET_INT_ARG(1); + break; + case CMD_LEVEL_NOTIME: + // Flag to if the time should be displayed 1 = no, else yes + level->notime = GET_INT_ARG(1); + break; + case CMD_LEVEL_NORESET: + // Flag to if the time should be reset when players respawn 1 = no, else yes + level->noreset = GET_INT_ARG(1); + break; + case CMD_LEVEL_NOSLOW: + + if(GET_INT_ARG(1)) + { + level->boss_slow = BOSS_SLOW_OFF; + } + + break; + case CMD_LEVEL_TYPE: + level->type = GET_INT_ARG(1); // Level type - 1 = bonus, else regular + level->nospecial = GET_INT_ARG(2); // Can use specials during bonus levels (default 0 - yes) + + if(GET_INT_ARG(3)) + { + level->nohurt = DAMAGE_FROM_ENEMY_OFF; + } + + break; + case CMD_LEVEL_NOHIT: + + if(GET_INT_ARG(1)) + { + level->nohit = DAMAGE_FROM_PLAYER_OFF; + } + + break; + case CMD_LEVEL_GRAVITY: + level->gravity = GET_FLOAT_ARG(1); + level->gravity /= 100; + break; + case CMD_LEVEL_MAXFALLSPEED: + level->maxfallspeed = GET_FLOAT_ARG(1); + level->maxfallspeed /= 10; + break; + case CMD_LEVEL_MAXTOSSSPEED: + level->maxtossspeed = GET_FLOAT_ARG(1); + level->maxtossspeed /= 10; + break; + case CMD_LEVEL_CAMERATYPE: + cameratype = GET_INT_ARG(1); + break; + case CMD_LEVEL_CAMERAOFFSET: + level->cameraxoffset = GET_INT_ARG(1); + level->camerazoffset = GET_INT_ARG(2); + break; + case CMD_LEVEL_SPAWN1: + case CMD_LEVEL_SPAWN2: + case CMD_LEVEL_SPAWN3: + case CMD_LEVEL_SPAWN4: + switch(cmd) + { + case CMD_LEVEL_SPAWN1: + i = 0; + break; + case CMD_LEVEL_SPAWN2: + i = 1; + break; + case CMD_LEVEL_SPAWN3: + i = 2; + break; + case CMD_LEVEL_SPAWN4: + i = 3; + break; + default: + assert(0); + } + + // Verify specified player index exists, + // then set values. + if(level->spawn && i < player_max) + { + level->spawn[i].x = GET_INT_ARG(1); + level->spawn[i].z = GET_INT_ARG(2); + level->spawn[i].y = GET_INT_ARG(3); + + if(level->spawn[i].y < 0) level->spawn[i].y = videomodes.vRes + 60; + } + + break; + case CMD_LEVEL_FRONTPANEL: + case CMD_LEVEL_PANEL: + if(level->numlayers == 0) + { + __realloc(level->layers, level->numlayers); + level->numlayers = 1; // reserve for background + } + + __realloc(level->layers, level->numlayers); + bgl = &(level->layers[level->numlayers]); + dm = &(bgl->drawmethod); + *dm = plainmethod; + + bgl->oldtype = (cmd == CMD_LEVEL_FRONTPANEL ? BGT_FRONTPANEL : BGT_PANEL); + + if(bgl->oldtype == BGT_PANEL) + { + bgl->order = panelcount + 1; + __realloc(panels, panelcount); + panels[panelcount++][0] = level->numlayers; + bgl->z = PANEL_Z; + bgl->ratio.x = 0; // x ratio + bgl->ratio.z = 0; // z ratio + dm->xrepeat = 1; // x repeat + } + else + { + frontpanels_loaded++; + bgl->z = FRONTPANEL_Z; + bgl->ratio.x = -0.4; // x ratio + bgl->ratio.z = 1; // z ratio + dm->xrepeat = -1; // x repeat + } + + bgl->bgspeedratio = 0; + bgl->offset.z = 0; + dm->yrepeat = 1; // z repeat + dm->config |= DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY; // transparency + bgl->enabled = 1; // enabled + bgl->quake = 1; // accept quake and rock + + load_layer(GET_ARG(1), maskPath, level->numlayers); + level->numlayers++; + + if(stricmp(GET_ARG(2), "none") != 0 && GET_ARG(2)[0]) + { + __realloc(level->layers, level->numlayers); + bgl = &(level->layers[level->numlayers]); + *bgl = *(bgl - 1); + panels[panelcount - 1][1] = level->numlayers; + + bgl->z = NEONPANEL_Z; + bgl->neon = 1; + bgl->gfx.handle = NULL; + load_layer(GET_ARG(2), maskPath, level->numlayers); + level->numlayers++; + } + + if(stricmp(GET_ARG(3), "none") != 0 && GET_ARG(3)[0]) + { + __realloc(level->layers, level->numlayers); + bgl = &(level->layers[level->numlayers]); + *bgl = *(bgl - 1); + panels[panelcount - 1][2] = level->numlayers; + dm = &(bgl->drawmethod); + + bgl->z = SCREENPANEL_Z; + bgl->neon = 0; + dm->alpha = BLEND_MODE_ALPHA; + bgl->gfx.handle = NULL; + load_layer(GET_ARG(3), maskPath, level->numlayers); + level->numlayers++; + } + break; + case CMD_LEVEL_STAGENUMBER: + current_stage = GET_INT_ARG(1); + break; + case CMD_LEVEL_ORDER: + // Append to order + value = GET_ARG(1); + i = 0; + while(value[i] ) + { + j = value[i]; + // WTF ? + if(j >= 'A' && j <= 'Z') + { + j -= 'A'; + } + else if(j >= 'a' && j <= 'z') + { + j -= 'a'; + } + else + { + errormessage = "Illegal character in panel order!"; + goto lCleanup; + } + __realloc(order, level->numpanels); + __realloc(level->panels, level->numpanels); + order[level->numpanels] = j; + level->numpanels++; + i++; + } + break; + case CMD_LEVEL_HOLE: + /*value = GET_ARG(1); // ltb 1-18-05 adjustable hole sprites + + if(holesprite < 0) + { + if(testpackfile(value, packfile) >= 0) + { + holesprite = loadsprite(value, 0, 0, pixelformat); // ltb 1-18-05 load new hole sprite + } + else + { + holesprite = loadsprite("data/sprites/hole", 0, 0, pixelformat); // ltb 1-18-05 no new sprite load the default + } + }*/ + + addhole(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7), GET_FLOAT_ARG(8), GET_FLOAT_ARG(9)); + break; + case CMD_LEVEL_WALL: + addwall(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7), GET_FLOAT_ARG(8), GET_FLOAT_ARG(9)); + break; + case CMD_LEVEL_BASEMAP: + addbasemap(GET_FLOAT_ARG(1), GET_FLOAT_ARG(2), GET_FLOAT_ARG(3), GET_FLOAT_ARG(4), GET_FLOAT_ARG(5), GET_FLOAT_ARG(6), GET_FLOAT_ARG(7)); + break; + case CMD_LEVEL_PALETTE: + __realloc(level->palettes, level->numpalettes); + if(!load_palette(level->palettes[level->numpalettes], GET_ARG(1))) + { + errormessage = "Failed to create colour conversion tables for level! (Out of memory?)"; + goto lCleanup; + } + level->numpalettes++; + break; + case CMD_LEVEL_UPDATESCRIPT: + case CMD_LEVEL_UPDATEDSCRIPT: + case CMD_LEVEL_UPDATELOGICSCRIPT: + case CMD_LEVEL_UPDATEDLOGICSCRIPT: + case CMD_LEVEL_KEYSCRIPT: + case CMD_LEVEL_LEVELSCRIPT: + case CMD_LEVEL_ENDLEVELSCRIPT: + switch(cmd) + { + case CMD_LEVEL_UPDATESCRIPT: + tempscript = &(level->update_script); + scriptname = "levelupdatescript"; + break; + case CMD_LEVEL_UPDATEDSCRIPT: + tempscript = &(level->updated_script); + scriptname = "levelupdatedscript"; + break; + case CMD_LEVEL_UPDATELOGICSCRIPT: + tempscript = &(level->update_logic_script); + scriptname = "levelupdatelogicscript"; + break; + case CMD_LEVEL_UPDATEDLOGICSCRIPT: + tempscript = &(level->updated_logic_script); + scriptname = "levelupdatedlogicscript"; + break; + case CMD_LEVEL_KEYSCRIPT: + tempscript = &(level->key_script); + scriptname = "levelkeyscript"; + break; + case CMD_LEVEL_LEVELSCRIPT: + tempscript = &(level->level_script); + scriptname = "levelscript"; + break; + case CMD_LEVEL_ENDLEVELSCRIPT: + tempscript = &(level->endlevel_script); + scriptname = "endlevelscript"; + break; + default: + assert(0); + + } + // this function is for model script, but it is OK for now + pos += lcmHandleCommandScripts(&arglist, buf + pos, tempscript, scriptname, filename, 1, 1); + break; + case CMD_LEVEL_BLOCKED: + exit_blocked = GET_INT_ARG(1); + break; + case CMD_LEVEL_ENDHOLE: + exit_hole = GET_INT_ARG(1); + break; + case CMD_LEVEL_WAIT: + // Clear spawn thing, set wait state instead + memset(&next, 0, sizeof(next)); + next.wait = 1; + break; + case CMD_LEVEL_NOJOIN: + // Clear spawn thing, set nojoin state instead + memset(&next, 0, sizeof(next)); + next.nojoin = 1; + break; + case CMD_LEVEL_CANJOIN: + memset(&next, 0, sizeof(next)); + next.nojoin = -1; + break; + case CMD_LEVEL_SHADOWCOLOR: + memset(&next, 0, sizeof(next)); + next.shadowcolor = parsecolor(GET_ARG(1)); + break; + case CMD_LEVEL_SHADOWALPHA: + memset(&next, 0, sizeof(next)); + next.shadowalpha = GET_INT_ARG(1); + if(blendfx_is_set == 0 && next.shadowalpha > 0) + { + blendfx[next.shadowalpha - 1] = 1; + } + break; + case CMD_LEVEL_SHADOWOPACITY: + memset(&next, 0, sizeof(next)); + next.shadowopacity = GET_INT_ARG(1); + break; + case CMD_LEVEL_LIGHT: + memset(&next, 0, sizeof(next)); + next.light.x = GET_INT_ARG(1); + next.light.y = GET_INT_ARG(2); + if(next.light.y == 0) + { + next.light.y = 64; + } + break; + case CMD_LEVEL_SCROLLZ: + memset(&next, 0, sizeof(next)); + next.scrollminz = GET_INT_ARG(1); + next.scrollmaxz = GET_INT_ARG(2); + next.scrollminz |= 0x80000000; + break; + case CMD_LEVEL_SCROLLX: + //shall we keep blockade? + memset(&next, 0, sizeof(next)); + next.scrollminx = GET_INT_ARG(1); + next.scrollmaxx = GET_INT_ARG(2); + next.scrollminx |= 0x80000000; + break; + case CMD_LEVEL_BLOCKADE: + // now x scroll can be limited by this + memset(&next, 0, sizeof(next)); + next.blockade = GET_INT_ARG(1); + if(next.blockade == 0) + { + next.blockade = -1; + } + break; + case CMD_LEVEL_SETPALETTE: + // change system palette + memset(&next, 0, sizeof(next)); + next.palette = GET_INT_ARG(1); + break; + case CMD_LEVEL_GROUP: + // Clear spawn thing, set group instead + memset(&next, 0, sizeof(next)); + next.groupmin = GET_INT_ARG(1); + next.groupmax = GET_INT_ARG(2); + if(next.groupmax < 1) + { + next.groupmax = 1; + } + if(next.groupmin < 1) + { + next.groupmin = 100; + } + break; + case CMD_LEVEL_SPAWN: + // Back to defaults + next.spawnplayer_count = 0; + memset(&next, 0, sizeof(next)); + next.index = next.item_properties.index = next.weaponindex = -1; + // Name of entry to be spawned + // Load model (if not loaded already) + cached_model = findmodel(GET_ARG(1)); +#ifdef DEBUG + printf("load_level: spawn %s, %s, cached: %p\n", GET_ARG(1), filename, cached_model); +#endif + if(cached_model) + { + tempmodel = cached_model; + } + else + { + tempmodel = load_cached_model(GET_ARG(1), filename, 0); + } + if(tempmodel) + { + next.name = tempmodel->name; + next.index = get_cached_model_index(next.name); + next.spawntype = SPAWN_TYPE_LEVEL; //2011_03_23, DC; Spawntype SPAWN_TYPE_LEVEL. + + crlf = 1; + } + break; + case CMD_LEVEL_2PSPAWN: + // Entity only for 2p game + next.spawnplayer_count = 1; + break; + case CMD_LEVEL_3PSPAWN: + // Entity only for 3p game + next.spawnplayer_count = 2; + break; + case CMD_LEVEL_4PSPAWN: + // Entity only for 4p game + next.spawnplayer_count = 3; + break; + case CMD_LEVEL_BOSS: + next.boss = GET_INT_ARG(1); + level->bossescount += next.boss ? 1 : 0; + level->numbosses = level->bossescount; + break; + + case CMD_LEVEL_FACTION_GROUP_DAMAGE_DIRECT: + + next.faction.damage_direct = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_GROUP_DAMAGE_INDIRECT: + + next.faction.damage_indirect = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_GROUP_HOSTILE: + + next.faction.hostile = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_GROUP_MEMBER: + + next.faction.member = faction_get_flags_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_TYPE_DAMAGE_DIRECT: + + next.faction.type_damage_direct = get_type_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_TYPE_DAMAGE_INDIRECT: + + next.faction.type_damage_indirect = get_type_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FACTION_TYPE_HOSTILE: + + next.faction.type_hostile = get_type_from_command_line(buf + pos); + break; + + case CMD_LEVEL_FLIP: + next.flip = GET_INT_ARG(1); + break; + case CMD_LEVEL_HEALTH: + next.health[0] = next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1); + break; + case CMD_LEVEL_2PHEALTH: + // Health the spawned entity will have if 2 people are playing + next.health[1] = next.health[2] = next.health[3] = GET_INT_ARG(1); + break; + case CMD_LEVEL_3PHEALTH: + // Health the spawned entity will have if 2 people are playing + next.health[2] = next.health[3] = GET_INT_ARG(1); //4player + break; + case CMD_LEVEL_4PHEALTH: + // Health the spawned entity will have if 2 people are playing + next.health[3] = GET_INT_ARG(1); //4player + break; + case CMD_LEVEL_MP: + // mp values to put max mp for player by tails + next.mp = GET_INT_ARG(1); + break; + case CMD_LEVEL_SCORE: + // So score can be overriden in the levels .txt file + if(!command_argument_get_int64(GET_ARG(1), &next.score)) + { + borShutdown( + 1, + "Invalid signed 64-bit score '%s' in %s, line %d.\n", + GET_ARG(1), + filename, + line + ); + } + next.multiple = GET_INT_ARG(2); + if(next.multiple == -1) + { + next.multiple = 0; // So negative values cannot be added + } + break; + case CMD_LEVEL_NOLIFE: + // Flag to determine if entity life is shown when hit + next.nolife = GET_INT_ARG(1); + break; + case CMD_LEVEL_ALIAS: + // Alias (name displayed) of entry to be spawned + strncpy(next.alias, GET_ARG(1), MAX_NAME_LEN - 1); + break; + case CMD_LEVEL_MAP: + // Colourmap for new entry + next.colourmap = GET_INT_ARG(1); + break; + case CMD_LEVEL_ALPHA: + // Item to be contained by new entry + next.alpha = GET_INT_ARG(1); + if(blendfx_is_set == 0 && next.alpha) + { + blendfx[next.alpha - 1] = 1; + } + break; + case CMD_LEVEL_DYING: + // Used to store which remake corresponds with the dying flash + next.dying = GET_INT_ARG(1); + next.per1 = GET_INT_ARG(2); + next.per2 = GET_INT_ARG(3); + next.dying2 = GET_INT_ARG(4); + break; + case CMD_LEVEL_ITEM: + case CMD_LEVEL_2PITEM: + case CMD_LEVEL_3PITEM: + case CMD_LEVEL_4PITEM: + switch(cmd) + { + // Item to be contained by new entry + case CMD_LEVEL_ITEM: + next.item_properties.player_count = 0; + break; + case CMD_LEVEL_2PITEM: + next.item_properties.player_count = 1; + break; + case CMD_LEVEL_3PITEM: + next.item_properties.player_count = 2; + break; + case CMD_LEVEL_4PITEM: + next.item_properties.player_count = 3; + break; + default: + assert(0); + } + // Load model (if not loaded already) + cached_model = findmodel(GET_ARG(1)); + if(cached_model) + { + tempmodel = cached_model; + } + else + { + tempmodel = load_cached_model(GET_ARG(1), filename, 0); + } + if(tempmodel) + { + next.item = tempmodel->name; + next.item_properties.index = get_cached_model_index(next.item); + } + break; + case CMD_LEVEL_ITEMMAP: + next.item_properties.colorset = GET_INT_ARG(1); + break; + case CMD_LEVEL_ITEMHEALTH: + next.item_properties.health = GET_INT_ARG(1); + break; + case CMD_LEVEL_ITEMALIAS: + strncpy(next.item_properties.alias, GET_ARG(1), MAX_NAME_LEN - 1); + break; + case CMD_LEVEL_WEAPON: + //spawn with a weapon 2007-2-12 by UTunnels + // Load model (if not loaded already) + cached_model = findmodel(GET_ARG(1)); + if(cached_model) + { + tempmodel = cached_model; + } + else + { + tempmodel = load_cached_model(GET_ARG(1), filename, 0); + } + if(tempmodel) + { + next.weapon = tempmodel->name; + next.weaponindex = get_cached_model_index(next.weapon); + } + break; + case CMD_LEVEL_AGGRESSION: + // Aggression can be set per spawn. + next.aggression = GET_INT_ARG(1); + break; + case CMD_LEVEL_CREDIT: + next.credit = GET_INT_ARG(1); + break; + case CMD_LEVEL_ITEMTRANS: + case CMD_LEVEL_ITEMALPHA: + next.item_properties.alpha = GET_INT_ARG(1); + break; + case CMD_LEVEL_COORDS: + next.position.x = GET_FLOAT_ARG(1); + next.position.z = GET_FLOAT_ARG(2); + next.position.y = GET_FLOAT_ARG(3); + break; + case CMD_LEVEL_SPAWNSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, &next.spawnscript, "Level spawn entry script", filename, 0, 1); + break; + case CMD_LEVEL_AT_SCRIPT: + if(!Script_IsInitialized(&next.spawnscript)) + { + Script_Init(&next.spawnscript, "Level spawn entry script", filename, 1); + } + fetchInlineScript(buf, &scriptbuf, &pos, &sblen); + if(!Script_AppendText(&next.spawnscript, scriptbuf, filename)) + { + errormessage = "Unable to parse level spawn entry script.\n"; + goto lCleanup; + } + free(scriptbuf); + scriptbuf = NULL; + break; + case CMD_LEVEL_AT: + // Place entry on queue + next.at = GET_INT_ARG(1); + + if(Script_IsInitialized(&next.spawnscript)) + { + Script_Compile(&next.spawnscript); + } + + __realloc(level->spawnpoints, level->numspawns); + memcpy(&level->spawnpoints[level->numspawns], &next, sizeof(next)); + level->numspawns++; + + // And clear... + memset(&next, 0, sizeof(next)); + break; + default: + if(command && command[0]) + { + if(!handle_txt_include(command, &arglist, &filename, fnbuf, &buf, &pos, &size)) + { + printf("Command '%s' not understood in file '%s'!\n", command, filename); + } + } + } + + // Go to next line + pos += getNewLineStart(buf + pos); + + if(isLoadingScreenTypeBar(bgPosi.set) || isLoadingScreenTypeBg(bgPosi.set)) + { + update_loading(&bgPosi, pos, size); + } + //update_loading(bgPosi[0]+videomodes.hShift, bgPosi[1]+videomodes.vShift, bgPosi[2], bgPosi[3]+videomodes.hShift, bgPosi[4]+videomodes.vShift, pos, size, bgPosi[5]); + else + { + update_loading(&loadingbg[1], pos, size); + } + } + + if(level->numpanels < 1) + { + errormessage = "Level error: level has no panels"; + goto lCleanup; + } + + if(bgPath[0]) + { + clearscreen(vscreen); + spriteq_clear(); + load_background(bgPath); + } + else if(background) + { + unload_background(); + } + + if(level->numlayers) + { + + for(i = 0; i < level->numlayers; i++) + { + bgl = &(level->layers[i]); + switch(bgl->oldtype) + { + case BGT_WATER: // default water hack + bgl->offset.z = background ? background->height : level->layers[0].size.y; + dm = &(bgl->drawmethod); + if(level->rocking) + { + dm->water.watermode = WATER_MODE_SHEAR; + dm->water.beginsize = 1.0; + dm->water.endsize = 1 + bgl->size.y / 11.0; + dm->water.perspective = WATER_PERSPECTIVE_NONE; + bgl->bgspeedratio = 2; + } + break; + case BGT_PANEL: + panel_width = bgl->size.x; + panel_height = bgl->size.y; + case BGT_FRONTPANEL: + if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN)) + { + bgl->ratio.z = 1; + } + break; + case BGT_BACKGROUND: + bgl->gfx.screen = background; + bgl->size.x = background->width; + bgl->size.y = background->height; + level->background = bgl; + break; + default: + break; + } + load_layer(NULL, NULL, i); + } + + if(level->background) + { + __realloc(level->layersref, level->numlayersref); + level->layersref[level->numlayersref] = *(level->background); + level->background = (s_layer *)(size_t)level->numlayersref++; + } + + + // non-panel type layers + for(i = 0; i < level->numlayers; i++) + { + bgl = &(level->layers[i]); + if(bgl->oldtype != BGT_PANEL && bgl->oldtype != BGT_BACKGROUND) + { + __realloc(level->layersref, level->numlayersref); + level->layersref[level->numlayersref] = *bgl; + bgl = &(level->layersref[level->numlayersref]); + switch(bgl->oldtype) + { + case BGT_BGLAYER: + __realloc(level->bglayers, level->numbglayers); + level->bglayers[level->numbglayers++] = (s_layer *)(size_t)level->numlayersref; + break; + case BGT_FGLAYER: + __realloc(level->fglayers, level->numfglayers); + level->fglayers[level->numfglayers++] = (s_layer *)(size_t)level->numlayersref; + break; + case BGT_WATER: + __realloc(level->waters, level->numwaters); + level->waters[level->numwaters++] = (s_layer *)(size_t)level->numlayersref; + break; + case BGT_GENERIC: + __realloc(level->genericlayers, level->numgenericlayers); + level->genericlayers[level->numgenericlayers++] = (s_layer *)(size_t)level->numlayersref; + break; + case BGT_FRONTPANEL: + bgl->offset.x = level->numfrontpanels * bgl->size.x; + bgl->spacing.x = (frontpanels_loaded - 1) * bgl->size.x; + __realloc(level->frontpanels, level->numfrontpanels); + level->frontpanels[level->numfrontpanels++] = (s_layer *)(size_t)level->numlayersref; + break; + default: + break; + } + level->numlayersref++; + } + } + + //panels, normal neon screen + for(i = 0; i < level->numpanels; i++) + { + for(j = 0; j < 3; j++) + { + if(panels[order[i]][j]) + { + __realloc(level->layersref, level->numlayersref); + level->layersref[level->numlayersref] = level->layers[panels[order[i]][j]]; + bgl = &(level->layersref[level->numlayersref]); + bgl->offset.x = panel_width * i; + level->panels[i][j] = (s_layer *)(size_t)level->numlayersref; + level->numlayersref++; + } + } + } + + //fix realloc junk pointers + bgl = level->layersref; + level->background = bgl + (size_t)level->background; + for(i = 0; i < level->numpanels; i++) + for(j = 0; j < 3; j++) + { + level->panels[i][j] = bgl + (size_t)level->panels[i][j]; + } + for(i = 0; i < level->numfrontpanels; i++) + { + level->frontpanels[i] = bgl + (size_t)level->frontpanels[i]; + } + for(i = 0; i < level->numbglayers; i++) + { + level->bglayers[i] = bgl + (size_t)level->bglayers[i]; + } + for(i = 0; i < level->numfglayers; i++) + { + level->fglayers[i] = bgl + (size_t)level->fglayers[i]; + } + for(i = 0; i < level->numgenericlayers; i++) + { + level->genericlayers[i] = bgl + (size_t)level->genericlayers[i]; + } + for(i = 0; i < level->numwaters; i++) + { + level->waters[i] = bgl + (size_t)level->waters[i]; + } + + } + + if(musicPath[0]) + { + music(musicPath, 1, musicOffset); + } + + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + level->width = level->numpanels * panel_width; + + if(level->width < videomodes.hRes) + { + level->width = videomodes.hRes; + } + + scrollmaxx = level->width - videomodes.hRes; + scrollmaxz = panel_height; + + if(level->scrolldir & SCROLL_LEFT) + { + advancex = (float)(level->width - videomodes.hRes); + } + else if(level->scrolldir & SCROLL_INWARD) + { + advancey = (float)(panel_height - videomodes.vRes); + } + + if(exit_blocked) + { + addwall(level->width - 30, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, MAX_WALL_HEIGHT + 1, 0); + } + if(exit_hole) + { + addhole(level->width, PLAYER_MAX_Z, -panel_height, 0, 1000, 1000, panel_height, 0, 0); + } + + if(crlf) + { + printf("\n"); + } + printf("Level Loaded: '%s'\n", level->name); + totalram = getSystemRam(BYTES); + freeram = getFreeRam(BYTES); + usedram = getUsedRam(BYTES); + printf("Total Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n Free Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n Used Ram: %11"PRIu64" Bytes ( %5"PRIu64" MB )\n", + totalram, + totalram >> 20, + freeram, + freeram >> 20, + usedram, + usedram >> 20); + printf("Total sprites mapped: %d\n\n", sprites_loaded); + +lCleanup: + + if (panels) + { + free(panels); + } + if (order) + { + free(order); + } + + if(buf) + { + free(buf); + } + if(scriptbuf) + { + free(scriptbuf); + } + + if(errormessage) + { + borShutdown(1, "ERROR: load_level, file %s, line %d, message: %s", filename, line, errormessage); + } +} + + + + + +///////////////////////////////////////////////////////////////////////////// +// Status // +///////////////////////////////////////////////////////////////////////////// +void bar(int x, int y, int value, int maxvalue, s_barstatus *pstatus) +{ + int max = 100; + int len; + int alphabg = 0; + int bgindex; + int colourindex; + int forex; + int forey; + int forew; + int foreh; + int bkw; + int bkh; + s_drawmethod dm = plainmethod; + + if(pstatus->config_flags & STATUS_CONFIG_GRAPH_VERTICAL) + { + max = pstatus->size.y; + } + else + { + max = pstatus->size.x; + } + + if (value > maxvalue) + { + value = maxvalue; + } + + if(pstatus->config_flags & STATUS_CONFIG_GRAPH_RATIO) + { + colourindex = colorbars ? (value * 5 / maxvalue + 1) : 2; + bgindex = colorbars ? 8 : 1; + len = value * max / maxvalue; + if (!len && value) + { + len = 1; + } + alphabg = BLEND_MULTIPLY + 1; + } + else + { + if (max > maxvalue) + { + max = maxvalue; + } + if (colorbars) + { + if (value <= max / 4) + { + bgindex = 0; + colourindex = 1; + } + else if (value <= max / 2) + { + bgindex = 0; + colourindex = 2; + } + else if (value <= max) + { + bgindex = 0; + colourindex = 3; + } + else + { + colourindex = value / (max + 1) + 3; + bgindex = colourindex - 1; + } + if (colourindex > 10) + { + colourindex = bgindex = 10; + } + } + else + { + colourindex = 2; + bgindex = value > max ? 5 : 1; + } + + len = value % max; + if (!len && value) + { + len = max; + } + alphabg = value > max ? 0 : (BLEND_MULTIPLY + 1); + } + + if(pstatus->config_flags & STATUS_CONFIG_GRAPH_VERTICAL) + { + forex = x; + forey = pstatus->config_flags & STATUS_CONFIG_GRAPH_INVERT ? y : (y + max - len); + bkw = forew = pstatus->size.x; + foreh = len; + bkh = max; + } + else + { + forex = pstatus->config_flags & STATUS_CONFIG_GRAPH_INVERT ? (x + max - len) : x; + forey = y; + forew = len; + bkw = max; + bkh = foreh = pstatus->size.y; + } + + if(!pstatus->colourtable) + { + pstatus->colourtable = &hpcolourtable; + } + + dm.alpha = alphabg; + spriteq_add_box(x + 1, y + 1, bkw, bkh, HUD_Z + 1 + pstatus->backlayer, (*pstatus->colourtable)[bgindex], &dm); + spriteq_add_box(forex + 1, forey + 1, forew, foreh, HUD_Z + 2 + pstatus->barlayer, (*pstatus->colourtable)[colourindex], NULL); + + if(!(pstatus->config_flags & STATUS_CONFIG_BORDER_DISABLE)) + { + spriteq_add_line(x, y, x + bkw + 1, y, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Top border. + spriteq_add_line(x, y + bkh + 1, x + bkw + 1, y + bkh + 1, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Bottom border. + spriteq_add_line(x, y + 1, x, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Left border. + spriteq_add_line(x + bkw + 1, y + 1, x + bkw + 1, y + bkh, HUD_Z + 3 + pstatus->borderlayer, color_white, NULL); //Right border. + spriteq_add_line(x, y + bkh + 2, x + bkw + 1, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Bottom shadow. + spriteq_add_line(x + bkw + 2, y + 1, x + bkw + 2, y + bkh + 2, HUD_Z + pstatus->borderlayer, color_black, NULL); //Right shadow. + } +} + +void goto_mainmenu(int flag) +{ + goto_mainmenu_flag = 1|(flag<<1); + escape_flag = flag; //Kratus (20-04-21) Added the new "escape" flag in the select screen, has the same effect as the esc key but now accessible by the "gotomainmenu" function +} + +void static backto_mainmenu() +{ + int i = 0; + s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32); + + copyscreen(pausebuffer, vscreen); + spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0); + spriteq_clear(); + spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0); + spriteq_lock(); + + sound_pause_music(1); + sound_pause_sample(1); + _pause = 2; + + if ( (goto_mainmenu_flag&1) ) goto_mainmenu_flag -= 1; + + update(1, 0); + + for(i = 0; i < MAX_PLAYERS; i++) + { + player[i].lives = 0; + } + endgame = 2; + //sound_pause_music(0); + //sound_pause_sample(0); + + _pause = 0; + bothnewkeys = 0; + spriteq_unlock(); + spriteq_clear(); + freescreen(&pausebuffer); + + return; +} + +void pausemenu() +{ + int pauselector = 0; + int quit = 0; + int controlp = 0, i; + key_mask_t newkeys; + s_set_entry *set = levelsets + current_set; + s_screen *pausebuffer = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32); + + copyscreen(pausebuffer, vscreen); + spriteq_draw(pausebuffer, 0, MIN_INT, MAX_INT, 0, 0); + spriteq_clear(); + spriteq_add_screen(0, 0, MIN_INT, pausebuffer, NULL, 0); + spriteq_lock(); + + for(i = 0; i < set->maxplayers; i++) + { + if(player[i].ent && (player[i].newkeys & FLAG_START)) + { + controlp = i; + break; + } + } + + _pause = 2; + bothnewkeys = 0; + while(!quit) + { + _menutextmshift(pauseoffset[4], -2, 0, pauseoffset[5], pauseoffset[6], Tr("Pause")); + _menutextmshift((pauselector == 0)?pauseoffset[1]:pauseoffset[0], -1, 0, pauseoffset[2], pauseoffset[3], Tr("Continue")); + _menutextmshift((pauselector == 1)?pauseoffset[1]:pauseoffset[0], 0, 0, pauseoffset[2], pauseoffset[3], Tr("End Game")); + + update(1, 0); + + newkeys = player[controlp].newkeys; + + if(newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN)) + { + pauselector ^= 1; + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(newkeys & FLAG_START) + { + if(pauselector) + { + for(i = 0; i < MAX_PLAYERS; i++) + { + player[i].lives = 0; + } + endgame = 2; + } + quit = 1; + sound_pause_music(0); + sound_pause_sample(0); + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + pauselector = 0; + } + if(newkeys & FLAG_ESC) + { + quit = 1; + sound_pause_music(0); + sound_pause_sample(0); + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + pauselector = 0; + } + if(newkeys & FLAG_SCREENSHOT) + { + _pause = 1; + sound_pause_music(1); + sound_pause_sample(1); + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + menu_options(); + } + } + _pause = 0; + bothnewkeys = 0; + spriteq_unlock(); + spriteq_clear(); + freescreen(&pausebuffer); +} + +unsigned getFPS(void) +{ + static u64 lasttick = 0, framerate = 0; + u64 curtick = timer_uticks(); + if(lasttick > curtick) + { + lasttick = curtick; + } + framerate = (framerate + (curtick - lasttick)) / 2; + lasttick = curtick; + if(!framerate) + { + // if the frame took 0 ms, act like it was 1 ms instead + return 1000; + } + + return round(1.0e6 / framerate); + +} + +void updatestatus() { + + uint64_t display_time; + uint64_t go_phase; + uint64_t i; + s_model *model = NULL; + s_set_entry *set = levelsets + current_set; + + for(i = 0; i < set->maxplayers; i++) { + + /* + * If the player is already in the game, do nothing. + */ + if(player[i].ent) { + continue; + } + + if(player[i].joining && player[i].name[0]) { + model = findmodel(player[i].name); + + if((player[i].playkeys & FLAG_ANYBUTTON || skipselect[i][0]) && !freezeall && !nojoin) { // Can't join while animations are frozen + player[i].lives = PLAYER_LIVES; // to address new lives settings + player[i].joining = 0; + player[i].hasplayed = 1; + player[i].spawnhealth = model->health; + player[i].spawnmp = model->mp; + + spawnplayer(i); + + execute_join_script(i); + + player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0; + + if(!nodropen) { + drop_all_enemies(); //27-12-2004 If drop enemies is on, drop all enemies + } + + if(!level->noreset) { + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + } + + } else if(player[i].playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)) { + + /* PLayer model selection. */ + + model = ((player[i].playkeys & FLAG_MOVELEFT) ? prevplayermodeln : nextplayermodeln)(model, i); + strcpy(player[i].name, model->name); + + player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmapn(model, -1, i) : 0; + + player[i].playkeys = 0; + + } else if(player[i].playkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect) { + + /* Player color seleciton. */ + + player[i].colourmap = ((player[i].playkeys & FLAG_MOVEUP) ? nextcolourmapn : prevcolourmapn)(model, player[i].colourmap, i); + + player[i].playkeys = 0; + } + + /* + * Player already joining. No reason to process + * any downstream logic in the scan. + */ + continue; + } + + /* + * Begin joining when the player has access to a credit. + */ + if( !nojoin && (player[i].credits || credits || (!player[i].hasplayed && noshare))) { + + if(player[i].playkeys & FLAG_START) { + player[i].lives = 0; + model = skipselect[i][0] ? findmodel(skipselect[i]) : nextplayermodeln(NULL, i); + strncpy(player[i].name, model->name, MAX_NAME_LEN); + + player[i].colourmap = (colourselect && (set->nosame & 2)) ? nextcolourmapn(model, -1, i) : 0; + + player[i].joining = 1; + player[i].disablekeys = player[i].playkeys = player[i].newkeys = player[i].releasekeys = 0; + + if(!level->noreset) { + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + } + + /* + * Player joining active game for first time + * and no credit sharing enabled. Start them + * with default number of continues before + * we deduct a credit from the player or global + * pool. + */ + if(!player[i].hasplayed && noshare) { + player[i].credits = CONTINUES; + } + + /* + * Subtract a credit from the player or global + * pool if cheats are not enabled. + */ + if(!(global_config.cheats & CHEAT_OPTIONS_CREDITS_ACTIVE)) { + + /* + * If credit sharing, deduct from the global + * pool, otherwise deduct from the player. + */ + if(noshare) { + --player[i].credits; + } else { + --credits; + } + + /* + * If continuescore is set to 1, reset the + * player's score to 0, otherwise if set to 2, + * increment the player's score by 1. + */ + if(set->continuescore == 1) { + player[i].score = 0; + } + + if(set->continuescore == 2) { + if(player[i].score < UINT64_MAX) { + player[i].score++; + } + } + } + } + } + } + + /* Get display time from time remaining and cap to 0 - 99 */ + display_time = (timeleft / global_config.counter_speed); + + if(display_time >= 99) { + display_time = 99; + oldtime = 99; + } + if(display_time <= 0) + { + display_time = 0; + oldtime = 99; + } + + if (is_total_timeover){ + timetoshow = 0; + } else { + timetoshow = display_time; + } + + if(timetoshow < oldtime || oldtime == 0) { + execute_timetick_script(timetoshow, go_time); + oldtime = timetoshow; + } + + if(display_time > 0 && !is_total_timeover) { + showtimeover = 0; + } + + + + if(go_time > _time) { + go_phase = (go_time - _time) % global_config.game_speed; + + if(go_phase < global_config.game_speed / 2) { + global_config.showgo = 1; + screen_status |= IN_SCREEN_SHOW_GO_ARROW; //Kratus (04-2022) Added the "showgo" event accessible by script + + if(gosound == 0 ) { + + if(global_sample_list.go >= 0) { + sound_play_sample(global_sample_list.go, 0, savedata.effectvol, savedata.effectvol, 100); // 26-12-2004 Play go sample as arrow flashes + } + + gosound = 1; // 26-12-2004 Sets sample as already played - stops sample repeating too much + } + + } else { + global_config.showgo = gosound = 0; //26-12-2004 Resets go sample after loop so it can be played again next time + screen_status &= ~IN_SCREEN_SHOW_GO_ARROW; //Kratus (04-2022) Added the "showgo" event accessible by script + } + + } else { + global_config.showgo = 0; + screen_status &= ~IN_SCREEN_SHOW_GO_ARROW; //Kratus (04-2022) Added the "showgo" event accessible by script + } +} + + +// Caskey, Damon V. +// 2016-11-16 +// +// Draw dot onto screen to indicate actual entity position, +// with text readout of Base, X, Y, and Z coordinates directly below. +void draw_properties_entity(entity *entity, int offset_z, int color, s_drawmethod *drawmethod) +{ + #define FONT_LABEL 1 + #define FONT_VALUE 0 + #define TEXT_MARGIN_Y 1 + #define OFFSET_LAYER -2 + + // Array keys for the list of items + // we want to display + enum + { + DRAW_PROPERTIES_KEY_NAME, + DRAW_PROPERTIES_KEY_BASE, + DRAW_PROPERTIES_KEY_POS, + DRAW_PROPERTIES_KEY_STATUS, + DRAW_PROPERTIES_ARRAY_SIZE // Array size, so always last. + }; + + typedef struct + { + s_axis_principal_int position; + s_axis_plane_vertical_int size; + } draw_coords; + + s_axis_plane_vertical_int screen_offset; // Base location calculated from screen offsets. + s_axis_plane_vertical_int base_pos; // Entity position with screen offsets applied. + draw_coords box; // On screen coords for display elements. + + int i; // Counter. + int str_offset_x; // Calculated offset of text for centering. + int label_width_max; + int str_width_max; // largest string width. + int str_height_max; // Largest string height. + size_t str_size; // Memory size of string. + + char *output_label[DRAW_PROPERTIES_ARRAY_SIZE]; + const char *output_format[DRAW_PROPERTIES_ARRAY_SIZE]; // Format ("%d, %s .."). + char *output_value[DRAW_PROPERTIES_ARRAY_SIZE]; // Final string to display position. + + // Let's build the format for information + // we want to display. + output_format[DRAW_PROPERTIES_KEY_NAME] = "%s"; + output_format[DRAW_PROPERTIES_KEY_BASE] = "%d"; + output_format[DRAW_PROPERTIES_KEY_POS] = "%d, %d, %d"; + output_format[DRAW_PROPERTIES_KEY_STATUS] = "%d, %d"; + + // Double pass method for unknown string size. + // + // 1. Build the label. + // + // 2. Attempt to copy 0 chars to unallocated + // buffer and record how many characters + // would be needed, plus 1 for the NULL terminator + // and record as a string_size. + // + // 3. Allocate memory using the string size. + // + // 4. Copy formatted string to allocated buffer + // for real. + // + // Repeat for each line item we want to display. + // + // TO: Work this into a loop. Main obstacle is + // the number of format string inputs vary depending + // on type of property. + + // Name + output_label[DRAW_PROPERTIES_KEY_NAME] = "Name: "; + output_value[DRAW_PROPERTIES_KEY_NAME] = NULL; + str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_NAME], 0, output_format[DRAW_PROPERTIES_KEY_NAME], entity->model->name) + 1; + output_value[DRAW_PROPERTIES_KEY_NAME] = malloc(str_size); + snprintf(output_value[DRAW_PROPERTIES_KEY_NAME], str_size, output_format[DRAW_PROPERTIES_KEY_NAME], entity->model->name); + + // Base + output_label[DRAW_PROPERTIES_KEY_BASE] = "Base: "; + output_value[DRAW_PROPERTIES_KEY_BASE] = NULL; + str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_BASE], 0, output_format[DRAW_PROPERTIES_KEY_BASE], (int)entity->base) + 1; + output_value[DRAW_PROPERTIES_KEY_BASE] = malloc(str_size); + snprintf(output_value[DRAW_PROPERTIES_KEY_BASE], str_size, output_format[DRAW_PROPERTIES_KEY_BASE], (int)entity->base); + + // XYZ + output_label[DRAW_PROPERTIES_KEY_POS] = "X,Y,Z: "; + output_value[DRAW_PROPERTIES_KEY_POS] = NULL; + str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_POS], 0, output_format[DRAW_PROPERTIES_KEY_POS], (int)entity->position.x, (int)entity->position.y, (int)entity->position.z) + 1; + output_value[DRAW_PROPERTIES_KEY_POS] = malloc(str_size); + snprintf(output_value[DRAW_PROPERTIES_KEY_POS], str_size, output_format[DRAW_PROPERTIES_KEY_POS], (int)entity->position.x, (int)entity->position.y, (int)entity->position.z); + + // HP & MP + output_label[DRAW_PROPERTIES_KEY_STATUS] = "HP, MP: "; + output_value[DRAW_PROPERTIES_KEY_STATUS] = NULL; + str_size = snprintf(output_value[DRAW_PROPERTIES_KEY_STATUS], 0, output_format[DRAW_PROPERTIES_KEY_STATUS], (int)entity->energy_state.health_current, (int)entity->energy_state.mp_current) + 1; + output_value[DRAW_PROPERTIES_KEY_STATUS] = malloc(str_size); + snprintf(output_value[DRAW_PROPERTIES_KEY_STATUS], str_size, output_format[DRAW_PROPERTIES_KEY_STATUS], (int)entity->energy_state.health_current, (int)entity->energy_state.mp_current); + + + // Get the largest string X and Y space. For X find the largest + // label and value, then add them. For Y, just get height of + // largest font. + label_width_max = font_string_width_max(output_label, DRAW_PROPERTIES_ARRAY_SIZE, FONT_LABEL); + str_width_max = label_width_max + font_string_width_max(output_value, DRAW_PROPERTIES_ARRAY_SIZE, FONT_VALUE); + + if (fontheight(FONT_LABEL) > fontheight(FONT_VALUE)) + { + str_height_max = fontheight(FONT_LABEL); + } + else + { + str_height_max = fontheight(FONT_VALUE); + } + + // Get our base offsets from screen vs. location. + screen_offset.x = screenx - ((entity->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_x_offset); + screen_offset.y = screeny - ((entity->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_y_offset); + + // Get entity position with screen offsets. + base_pos.x = entity->position.x - screen_offset.x; + base_pos.y = (entity->position.z - offset_z) - entity->position.y - screen_offset.y; + + // Get a value of half the text width. + // We can use this to center our text + // on the entity. + str_offset_x = str_width_max / 2; + + // Apply text offset. + box.position.x = base_pos.x - str_offset_x; + + box.position.y = base_pos.y; + box.position.z = entity->position.z + offset_z; + + // Draw position dot. + // The +1 to Z is a quick fix - offset_z + // distorts the dot's vertical position + // instead of just adjusting Z. + spriteq_add_dot(base_pos.x, base_pos.y, box.position.z+1, color, drawmethod); + + // Print each item text. + for(i = 0; i < DRAW_PROPERTIES_ARRAY_SIZE; i++) + { + // If the item string exists then + // we can find a position, print it to + // the screen, and free up allocated memory. + if(output_value[i]) + { + // Add font height and margin to Y position. + base_pos.y += (str_height_max + TEXT_MARGIN_Y); + + // Print label to the screen. The value X + // position adds maximum label width, plus + // 25% the width a single value character. + font_printf(box.position.x, base_pos.y, FONT_LABEL, OFFSET_LAYER, output_label[i]); + font_printf(box.position.x + label_width_max + (fontmonowidth(FONT_VALUE) * 0.25), base_pos.y, FONT_VALUE, OFFSET_LAYER, output_value[i]); + + // Release memory allocated for the value strings. + free(output_value[i]); + } + } + + return; + + // Remove local constants. + #undef FONT + #undef TEXT_MARGIN_Y + #undef OFFSET_LAYER +} + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Screen-space debug box coordinates. +*/ +typedef struct s_debug_draw_box { + s_axis_principal_int position; + s_axis_plane_vertical_int size; +} s_debug_draw_box; + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Convert an entity-relative box into screen-space +* draw coordinates. +*/ +static void debug_box_get_screen_coordinates(entity* const target_entity, + const int pos_x, + const int pos_y, + const int pos_z, + const int size_w, + const int size_h, + const int offset_z, + s_debug_draw_box* const box) { + + s_axis_plane_vertical_int screen_offset; + s_axis_plane_vertical_int base_pos; + + /* + * Guard against invalid targets. The caller should + * already validate these, but keep this helper safe. + */ + if (!target_entity || !box) { + return; + } + + /* + * Get our base offsets from screen vs. world location. + */ + screen_offset.x = screenx - ((target_entity->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_x_offset); + screen_offset.y = screeny - ((target_entity->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_y_offset); + + /* + * Convert entity world position to screen position. + */ + base_pos.x = target_entity->position.x - screen_offset.x; + base_pos.y = (target_entity->position.z - offset_z) - target_entity->position.y - screen_offset.y; + + /* + * Apply box coordinates. + */ + box->size.x = size_w - pos_x; + + /* + * Accommodate entity direction. + */ + if (target_entity->direction == DIRECTION_RIGHT) { + box->position.x = base_pos.x + pos_x; + } + else { + box->position.x = base_pos.x - (box->size.x + pos_x); + } + + box->position.y = base_pos.y + pos_y; + box->size.y = (base_pos.y + size_h) - box->position.y; + box->position.z = pos_z + offset_z; +} + +/* +* Convert an entity-relative box into screen-space coordinates, +* then apply visual-only perspective offset while preserving +* the requested draw layer. +*/ +static void debug_box_get_screen_coordinates_at_layer(entity* const target_entity, + const int pos_x, + const int pos_y, + const int draw_layer, + const int size_w, + const int size_h, + const int visual_offset_x, + const int visual_offset_y, + s_debug_draw_box* const box) { + + if (!box) { + return; + } + + debug_box_get_screen_coordinates( + target_entity, + pos_x, + pos_y, + draw_layer, + size_w, + size_h, + 0, + box); + + box->position.x += visual_offset_x; + box->position.y += visual_offset_y; + box->position.z = draw_layer; +} + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Draw a filled transparent debug box with a hard +* one-pixel border in the same color. +*/ +static void debug_box_draw_filled_with_border(const s_debug_draw_box* const box, const int color, s_drawmethod* const drawmethod) { + int far_x; + int far_y; + + if (!box) { + return; + } + + /* + * Draw the transparent fill first. + */ + spriteq_add_box( + box->position.x, + box->position.y, + box->size.x, + box->size.y, + box->position.z, + color, + drawmethod); + + /* + * Then draw a hard outline over the fill. + */ + far_x = box->position.x + (box->size.x - 1); + far_y = box->position.y + box->size.y; + + spriteq_add_line(box->position.x, box->position.y, far_x, box->position.y, box->position.z, color, NULL); /* Top. */ + spriteq_add_line(box->position.x, far_y, far_x, far_y, box->position.z, color, NULL); /* Bottom. */ + spriteq_add_line(box->position.x, box->position.y, box->position.x, far_y, box->position.z, color, NULL); /* Left. */ + spriteq_add_line(far_x, box->position.y, far_x, far_y, box->position.z, color, NULL); /* Right. */ +} + +/* +* Caskey, Damon V. +* 2016-11-16 +* +* Convert an entity-relative box to screen-space and +* draw it as a transparent rectangle with a hard border. +*/ +void draw_box_on_entity(entity* entity, int pos_x, int pos_y, int pos_z, int size_w, int size_h, int offset_z, int color, s_drawmethod* drawmethod) { + s_debug_draw_box box = { 0 }; + + debug_box_get_screen_coordinates( + entity, + pos_x, + pos_y, + pos_z, + size_w, + size_h, + offset_z, + &box); + + debug_box_draw_filled_with_border(&box, color, drawmethod); +} + +/* +* Caskey, Damon V. +* 2026-07-06 +* +* Draw a wireframe rectangle from prepared debug box coordinates. +*/ +static void debug_box_draw_wireframe(const s_debug_draw_box* const box, const int color, s_drawmethod* const drawmethod) { + int left; + int right; + int top; + int bottom; + int layer; + + if (!box) { + return; + } + + left = box->position.x; + top = box->position.y; + right = box->position.x + box->size.x - 1; + bottom = box->position.y + box->size.y - 1; + layer = box->position.z; + + spriteq_add_line(left, top, right, top, layer, color, drawmethod); + spriteq_add_line(right, top, right, bottom, layer, color, drawmethod); + spriteq_add_line(right, bottom, left, bottom, layer, color, drawmethod); + spriteq_add_line(left, bottom, left, top, layer, color, drawmethod); +} + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Draw a collision hitbox as a simple perspective cube. +* +* The entity's own Z position is the visual and draw-layer +* anchor. Higher draw-layer values are closer to the screen. +* +* Background Z is drawn behind the entity's Z layer. +* Foreground Z is drawn in front of the entity's Z layer. +* +* The screen-space perspective offset is intentionally +* illustrative. It keeps the true X/Y extent centered around +* the entity's Z position while making the depth readable. +* +* 2026-07-02: Disabled until debug menu option implemented. +*/ +static void draw_collision_cube_on_entity(entity* const target_entity, + const s_hitbox* const coords, + const int color, + s_drawmethod* const drawmethod) { + + s_debug_draw_box back_box = { 0 }; + s_debug_draw_box front_box = { 0 }; + + int entity_z; + int z_background; + int z_foreground; + int z_depth; + + int perspective_x_sign; + int perspective_offset_back; + int perspective_offset_front; + int back_layer; + int front_layer; + + int back_left; + int back_right; + int back_top; + int back_bottom; + + int front_left; + int front_right; + int front_top; + int front_bottom; + + if (!target_entity || !coords) { + return; + } + + entity_z = target_entity->position.z; + + z_background = coords->z_background; + z_foreground = coords->z_foreground; + + if (z_background < 0) { + z_background = 0; + } + + if (z_foreground < 0) { + z_foreground = 0; + } + + z_depth = z_background + z_foreground; + + back_layer = entity_z - z_background; + front_layer = entity_z + z_foreground; + + /* + * No depth - draw a wireframe rectangle only. + */ + if (z_depth < 1) { + debug_box_get_screen_coordinates_at_layer( + target_entity, + coords->x, + coords->y, + entity_z + 1, + coords->width, + coords->height, + 0, + 0, + &front_box); + + debug_box_draw_wireframe(&front_box, color, NULL); + return; + } + + /* + * Visual-only perspective. Keep this independent from + * the true Z layer. + * + */ + perspective_offset_back = z_background / 4; + perspective_offset_front = z_foreground / 4; + + if (z_background > 0 && perspective_offset_back < 1) { + perspective_offset_back = 1; + } + + if (z_foreground > 0 && perspective_offset_front < 1) { + perspective_offset_front = 1; + } + + /* + * Mirror only the visual perspective X offset. + * The collision box coordinate conversion already handles + * entity direction, so do not modify coords->x or coords->width. + */ + perspective_x_sign = (target_entity->direction == DIRECTION_RIGHT) ? 1 : -1; + + /* + * Back face: projected from entity Z toward background. + * Front face: projected from entity Z toward foreground. + */ + debug_box_get_screen_coordinates_at_layer( + target_entity, + coords->x, + coords->y, + back_layer, + coords->width, + coords->height, + perspective_offset_back * perspective_x_sign, + -perspective_offset_back, + &back_box); + + debug_box_get_screen_coordinates_at_layer( + target_entity, + coords->x, + coords->y, + front_layer, + coords->width, + coords->height, + -perspective_offset_front * perspective_x_sign, + perspective_offset_front, + &front_box); + + debug_box_draw_wireframe(&back_box, color, NULL); + debug_box_draw_wireframe(&front_box, color, NULL); + + back_left = back_box.position.x; + back_top = back_box.position.y; + back_right = back_box.position.x + back_box.size.x - 1; + back_bottom = back_box.position.y + back_box.size.y - 1; + + front_left = front_box.position.x; + front_top = front_box.position.y; + front_right = front_box.position.x + front_box.size.x - 1; + front_bottom = front_box.position.y + front_box.size.y - 1; + + /* + * Connector lines use the front layer so they remain visible. + */ + spriteq_add_line(back_left, back_top, front_left, front_top, front_layer, color, NULL); + spriteq_add_line(back_right, back_top, front_right, front_top, front_layer, color, NULL); + spriteq_add_line(back_left, back_bottom, front_left, front_bottom, front_layer, color, NULL); + spriteq_add_line(back_right, back_bottom, front_right, front_bottom, front_layer, color, NULL); +} + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Draw every active collision box in a collection +* for visual debug output. +*/ +static void draw_visual_collision_collection(entity* const target_entity, + const s_collision_collection* const collection, + const int color, + s_drawmethod* const drawmethod, + const e_debug_display flat_bit, + const e_debug_display projected_bit) { + + const s_collision_instance* collision = NULL; + const s_hitbox* coords = NULL; + uint64_t active_status; + int collision_index; + + if (!target_entity || !collection || !collection->active_status) { + return; + } + + active_status = collection->active_status; + + while (active_status) { + collision_index = collision_get_lowest_active_index(active_status); + active_status &= active_status - 1; + + collision = collection->slots[collision_index]; + + if (!collision) { + continue; + } + + coords = &collision->coords; + + if (!collision_check_has_coords(coords)) { + continue; + } + + if (savedata.debuginfo & flat_bit) { + draw_box_on_entity( + target_entity, + coords->x, + coords->y, + target_entity->position.z + 1, + coords->width, + coords->height, + 2, + color, + drawmethod); + } + + if (savedata.debuginfo & projected_bit) { + draw_collision_cube_on_entity( + target_entity, + coords, + color, + drawmethod); + } + } +} + +/* +* Caskey, Damon V. +* Unknown date ~2018 +* +* Draw collision on screen as visual boxes. +*/ +void draw_visual_debug(void) { + #define LOCAL_COLOR_BLUE _makecolour(0, 0, 255) + #define LOCAL_COLOR_GREEN _makecolour(0, 255, 0) + #define LOCAL_COLOR_MAGENTA _makecolour(255, 0, 255) + #define LOCAL_COLOR_WHITE _makecolour(255, 255, 255) + + int entity_index; + int range_y_min = 0; + int range_y_max = 0; + int animation_frame = 0; + + s_drawmethod drawmethod = plainmethod; + s_collision_collection* collection = NULL; + entity* target_entity = NULL; + + drawmethod.alpha = BLEND_MODE_ALPHA; + + for (entity_index = 0; entity_index < ent_max; entity_index++) { + target_entity = ent_list[entity_index]; + + /* + * Entity must exist. + */ + if (!target_entity) { + continue; + } + + /* + * Entity must be alive. + */ + if (target_entity->death_state & DEATH_STATE_DEAD) { + continue; + } + + /* + * Most visual debug drawing depends on animation + * frame data. + */ + if (!target_entity->animation) { + continue; + } + + animation_frame = target_entity->animpos; + + /* + * Protect collision frame table lookups. + */ + if (animation_frame < 0 || animation_frame >= target_entity->animation->numframes) { + continue; + } + + /* + * Basic properties: name, position, HP, etc. + */ + if (savedata.debuginfo & DEBUG_DISPLAY_PROPERTIES) { + draw_properties_entity(target_entity, 0, LOCAL_COLOR_WHITE, NULL); + } + + /* + * Range debug requested? + */ + if (savedata.debuginfo & DEBUG_DISPLAY_RANGE) { + + /* + * Range is calculated a bit differently than + * body/attack boxes. For Y axis, invert the + * value and place min/max into the opposite + * drawing parameters. + */ + range_y_min = -target_entity->animation->range.y.min; + range_y_max = -target_entity->animation->range.y.max; + + draw_box_on_entity( + target_entity, + target_entity->animation->range.x.min, + range_y_max, + target_entity->position.z + 1, + target_entity->animation->range.x.max, + range_y_min, + -1, + LOCAL_COLOR_GREEN, + &drawmethod); + } + + /* + * Collision attack debug requested? + */ + if ((savedata.debuginfo & (DEBUG_DISPLAY_COLLISION_ATTACK_2D | DEBUG_DISPLAY_COLLISION_ATTACK_3D)) + && target_entity->animation->collision_attack) { + + collection = target_entity->animation->collision_attack[animation_frame]; + + draw_visual_collision_collection( + target_entity, + collection, + LOCAL_COLOR_MAGENTA, + &drawmethod, + DEBUG_DISPLAY_COLLISION_ATTACK_2D, + DEBUG_DISPLAY_COLLISION_ATTACK_3D); + } + + /* + * Collision body debug requested? + */ + if ((savedata.debuginfo & (DEBUG_DISPLAY_COLLISION_BODY_2D | DEBUG_DISPLAY_COLLISION_BODY_3D)) + && target_entity->animation->collision_body) { + + collection = target_entity->animation->collision_body[animation_frame]; + + draw_visual_collision_collection( + target_entity, + collection, + LOCAL_COLOR_BLUE, + &drawmethod, + DEBUG_DISPLAY_COLLISION_BODY_2D, + DEBUG_DISPLAY_COLLISION_BODY_3D); + } + + if ((savedata.debuginfo & (DEBUG_DISPLAY_COLLISION_SPACE_2D | DEBUG_DISPLAY_COLLISION_SPACE_3D)) + && target_entity->animation->collision_space) { + + collection = target_entity->animation->collision_space[animation_frame]; + + draw_visual_collision_collection( + target_entity, + collection, + LOCAL_COLOR_GREEN, + &drawmethod, + DEBUG_DISPLAY_COLLISION_SPACE_2D, + DEBUG_DISPLAY_COLLISION_SPACE_3D); + } + } + + #undef LOCAL_COLOR_BLUE + #undef LOCAL_COLOR_GREEN + #undef LOCAL_COLOR_MAGENTA + #undef LOCAL_COLOR_WHITE +} + + +void predrawstatus() +{ + + int icon = 0; + int i; + uint64_t tmp; + s_set_entry *set = levelsets + current_set; + s_model *model = NULL; + s_drawmethod drawmethod = plainmethod; + + if(bgicon >= 0) + { + spriteq_add_sprite(videomodes.hShift + bgicon_offsets[0], savedata.windowpos + bgicon_offsets[1], bgicon_offsets[2], bgicon, NULL, 0); + } + if(olicon >= 0) + { + spriteq_add_sprite(videomodes.hShift + olicon_offsets[0], savedata.windowpos + olicon_offsets[1], olicon_offsets[2], olicon, NULL, 0); + } + + for(i = 0; i < set->maxplayers; i++) + { + if(player[i].ent) + { + tmp = player[i].score; //work around issue on 64bit where sizeof(long) != sizeof(int) + if(!pscore[i][2] && !pscore[i][3] && !pscore[i][4] && !pscore[i][5]) + { + font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1], pscore[i][6], 0, (scoreformat ? "%s - %09" PRIu64 : "%s - %" PRIu64), (char *)(player[i].ent->name), tmp); + } + else + { + font_printf(videomodes.shiftpos[i] + pscore[i][0], savedata.windowpos + pscore[i][1], pscore[i][6], 0, "%s", player[i].ent->name); + font_printf(videomodes.shiftpos[i] + pscore[i][2], savedata.windowpos + pscore[i][3], pscore[i][6], 0, "-"); + font_printf(videomodes.shiftpos[i] + pscore[i][4], savedata.windowpos + pscore[i][5], pscore[i][6], 0, (scoreformat ? "%09" PRIu64 : "%" PRIu64), tmp); + } + + if(player[i].ent->energy_state.health_current <= 0) + { + icon = player[i].ent->modeldata.icon.die; + } + else if(player[i].ent->inpain & IN_PAIN_HIT) + { + icon = player[i].ent->modeldata.icon.pain; + } + else if(player[i].ent->getting) + { + icon = player[i].ent->modeldata.icon.get; + } + else + { + icon = player[i].ent->modeldata.icon.def; + } + + if(icon >= 0) + { + drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL; + spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], savedata.windowpos + picon[i][1], 10000, icon, &drawmethod, 0); + } + + if(player[i].ent->weapent) + { + if(player[i].ent->weapent->modeldata.icon.weapon >= 0) + { + drawmethod.table = player[i].ent->weapent->modeldata.icon.usemap ? player[i].ent->weapent->colourmap : NULL; + spriteq_add_sprite(videomodes.shiftpos[i] + piconw[i][0], savedata.windowpos + piconw[i][1], 10000, player[i].ent->weapent->modeldata.icon.weapon, &drawmethod, 0); + } + + if(player[i].ent->weapent->modeldata.weapon_properties.weapon_state & WEAPON_STATE_LIMITED_USE && player[i].ent->weapent->modeldata.weapon_properties.use_count) + { + font_printf(videomodes.shiftpos[i] + pshoot[i][0], savedata.windowpos + pshoot[i][1], pshoot[i][2], 0, "%u", player[i].ent->weapent->modeldata.weapon_properties.use_count); + } + } + + /* + * If the player has MP, let's try to generate + * icons as well. + */ + + if (player[i].ent->modeldata.mp) { + + drawmethod.table = player[i].ent->modeldata.icon.usemap ? player[i].ent->colourmap : NULL; + + const float mp_percentage = (float)player[i].ent->energy_state.mp_old / player[i].ent->modeldata.mp; + + int mp_icon_sprite = ICON_NONE; + + if (mp_percentage <= 0.0 && player[i].ent->modeldata.icon.mpnone != ICON_NONE){ + mp_icon_sprite = player[i].ent->modeldata.icon.mpnone; + } + else if (mp_percentage < 0.25 && player[i].ent->modeldata.icon.mplow != ICON_NONE){ + mp_icon_sprite = player[i].ent->modeldata.icon.mplow; + } + else if (mp_percentage < 0.75 && player[i].ent->modeldata.icon.mpmed != ICON_NONE){ + mp_icon_sprite = player[i].ent->modeldata.icon.mpmed; + } + else if (mp_percentage < 1.0 && player[i].ent->modeldata.icon.mphigh != ICON_NONE){ + mp_icon_sprite = player[i].ent->modeldata.icon.mphigh; + } + else if (mp_percentage >= 1.0 && player[i].ent->modeldata.icon.mpmax != ICON_NONE){ + mp_icon_sprite = player[i].ent->modeldata.icon.mpmax; + } + + /* + * Draw an icon if we found one. + */ + if (mp_icon_sprite > 0) + { + spriteq_add_sprite(videomodes.shiftpos[i] + mpicon[i][0], savedata.windowpos + mpicon[i][1], 10000, mp_icon_sprite, &drawmethod, 0); + } + } + + font_printf(videomodes.shiftpos[i] + plifeX[i][0], savedata.windowpos + plifeX[i][1], plifeX[i][2], 0, "x"); + font_printf(videomodes.shiftpos[i] + plifeN[i][0], savedata.windowpos + plifeN[i][1], plifeN[i][2], 0, "%i", player[i].lives); + + if(rush[0] && player[i].ent->rush.count > 1 && _time <= player[i].ent->rush.time) + { + font_printf(videomodes.shiftpos[i] + prush[i][0], prush[i][1], rush[2], 0, "%s", rush_names[0]); + font_printf(videomodes.shiftpos[i] + prush[i][2], prush[i][3], rush[3], 0, "%i", player[i].ent->rush.count); + + if(rush[0] != 2) + { + font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]); + font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.max); + } + } + + if(rush[0] == 2) + { + font_printf(videomodes.shiftpos[i] + prush[i][4], prush[i][5], rush[4], 0, "%s", rush_names[1]); + font_printf(videomodes.shiftpos[i] + prush[i][6], prush[i][7], rush[5], 0, "%i", player[i].ent->rush.max); + } + + if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife) + { + // Displays life unless overridden by flag + font_printf(videomodes.shiftpos[i] + ename[i][0], savedata.windowpos + ename[i][1], ename[i][2], 0, player[i].ent->opponent->name); + if(player[i].ent->opponent->energy_state.health_current <= 0) + { + icon = player[i].ent->opponent->modeldata.icon.die; + } + else if(player[i].ent->opponent->inpain & IN_PAIN_HIT) + { + icon = player[i].ent->opponent->modeldata.icon.pain; + } + else if(player[i].ent->opponent->getting) + { + icon = player[i].ent->opponent->modeldata.icon.get; + } + else + { + icon = player[i].ent->opponent->modeldata.icon.def; + } + + if(icon >= 0) + { + drawmethod.table = player[i].ent->opponent->modeldata.icon.usemap ? player[i].ent->opponent->colourmap : NULL; + spriteq_add_sprite(videomodes.shiftpos[i] + eicon[i][0], savedata.windowpos + eicon[i][1], 10000, icon, &drawmethod, 0); // Feb 26, 2005 - Changed to opponent->map so icons don't pallete swap with die animation + } + } + } + else if(player[i].joining && player[i].name[0]) + { + model = findmodel(player[i].name); + font_printf(videomodes.shiftpos[i] + pnameJ[i][0], savedata.windowpos + pnameJ[i][1], pnameJ[i][6], 0, player[i].name); + if(nojoin) + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Please Wait")); + } + else + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][2], savedata.windowpos + pnameJ[i][3], pnameJ[i][6], 0, Tr("Select Hero")); + } + icon = model->icon.def; + + if(icon >= 0) + { + drawmethod.table = model->icon.usemap ? model_get_colourmap(model, player[i].colourmap) : NULL; + spriteq_add_sprite(videomodes.shiftpos[i] + picon[i][0], picon[i][1], 10000, icon, &drawmethod, 0); + } + } + else if(player[i].credits || credits || (!player[i].hasplayed && noshare)) + { + if(player[i].credits && ((_time / (global_config.game_speed * 2)) & 1)) + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), player[i].credits); + } + else if(credits && ((_time / (global_config.game_speed * 2)) & 1)) + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), credits); + } + else if(!player[i].hasplayed && ((_time / (global_config.game_speed * 2)) & 1)) + { + int showcredits = (!noshare) ? credits : CONTINUES; + + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Credit %i"), showcredits); + } + else if(nojoin) + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Please Wait")); + } + else + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("Press Start")); + } + } + else + { + font_printf(videomodes.shiftpos[i] + pnameJ[i][4], savedata.windowpos + pnameJ[i][5], pnameJ[i][6], 0, Tr("GAME OVER")); + } + }// end of for + + // If any of the debug flags are enabled, let's + // output debug data to end user. + if(savedata.debuginfo) + { + draw_visual_debug(); + } + + if(timeicon >= 0) + { + spriteq_add_sprite(videomodes.hShift + timeicon_offsets[0], savedata.windowpos + timeicon_offsets[1], 10000, timeicon, NULL, 0); + } + if(!level->notime) + { + font_printf(videomodes.hShift + timeloc[0] + 2, savedata.windowpos + timeloc[1] + 2, timeloc[5], 0, "%02i", timetoshow); + } + if(showtimeover) + { + font_printf(videomodes.hShift + 113, videomodes.vShift + savedata.windowpos + 110, timeloc[5], 0, Tr("TIME OVER")); + } + + if(global_config.showgo) + { + if(level->scrolldir & SCROLL_LEFT) //TODO: upward and downward go + { + + if(golsprite >= 0) + { + spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, golsprite, NULL, 0); // new sprite for left direction + } + else + { + drawmethod.table = 0; + drawmethod.config |= DRAWMETHOD_CONFIG_FLIP_X; + spriteq_add_sprite(40, 60 + videomodes.vShift, 10000, gosprite, &drawmethod, 0); + } + } + else if(level->scrolldir & SCROLL_RIGHT) + { + spriteq_add_sprite(videomodes.hRes - 40, 60 + videomodes.vShift, 10000, gosprite, NULL, 0); + } + } + + // Performance info. + if(savedata.debuginfo & DEBUG_DISPLAY_PERFORMANCE) + { + spriteq_add_box(0, videomodes.dOffset - 12, videomodes.hRes, videomodes.dOffset + 12, LAYER_Z_LIMIT_BOX_MAX, 0, NULL); + font_printf(2, videomodes.dOffset - 10, 0, LAYER_Z_LIMIT_MAX, Tr("FPS: %03d"), getFPS()); + font_printf(videomodes.hRes / 2, videomodes.dOffset - 10, 0, LAYER_Z_LIMIT_MAX, Tr("Free Ram: %s KB"), commaprint(freeram / KBYTES)); + font_printf(2, videomodes.dOffset, 0, LAYER_Z_LIMIT_MAX, Tr("Sprites: %d / %d"), spriteq_get_sprite_count(), spriteq_get_sprite_max()); + font_printf(videomodes.hRes / 2, videomodes.dOffset, 0, LAYER_Z_LIMIT_MAX, Tr("Used Ram: %s KB"), commaprint(usedram / KBYTES)); + } +} + +/* +* Caskey, Damon V. +* 2023-03-08 +* +* Allocate an object. +*/ +s_barstatus* bar_status_allocate_object() +{ + s_barstatus* result; + + /* Allocate memory with 0 values and get the pointer. */ + result = calloc(1, sizeof(*result)); + + return result; +} + +// draw boss status on screen +void drawenemystatus(entity *ent) +{ + s_drawmethod drawmethod; + s_barstatus* status; + int icon; + + status = ent->modeldata.hud_popup; + + if(status->name_position.x > -1000 && status->name_position.y > -1000) + { + font_printf(status->name_position.x, status->name_position.y, 0, 0, "%s", ent->name); + } + + if(ent->modeldata.icon.position.x > -1000 && ent->modeldata.icon.position.y > -1000) + { + if(ent->energy_state.health_current <= 0) + { + icon = ent->modeldata.icon.die; + } + else if(ent->inpain & IN_PAIN_HIT) + { + icon = ent->modeldata.icon.pain; + } + else if(ent->getting) + { + icon = ent->modeldata.icon.get; + } + else + { + icon = ent->modeldata.icon.def; + } + + if(icon >= 0) + { + drawmethod = plainmethod; + drawmethod.table = ent->modeldata.icon.usemap ? ent->colourmap : NULL; + spriteq_add_sprite(ent->modeldata.icon.position.x, ent->modeldata.icon.position.y, HUD_Z, icon, &drawmethod, 0); + } + } + + if(ent->modeldata.health && status->graph_position.x > -1000 && status->graph_position.y > -1000) + { + bar(status->graph_position.x, status->graph_position.y, ent->energy_state.health_old, ent->modeldata.health, status); + } +} + + +void drawstatus() +{ + int i; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if(player[i].ent) + { + // Health bars + bar(videomodes.shiftpos[i] + plife[i][0], savedata.windowpos + plife[i][1], player[i].ent->energy_state.health_old, player[i].ent->modeldata.health, &lbarstatus); + if(player[i].ent->opponent && !player[i].ent->opponent->modeldata.nolife && player[i].ent->opponent->modeldata.health) + { + bar(videomodes.shiftpos[i] + elife[i][0], savedata.windowpos + elife[i][1], player[i].ent->opponent->energy_state.health_old, player[i].ent->opponent->modeldata.health, &olbarstatus); // Tied in with the nolife flag + } + // Draw mpbar + if(player[i].ent->modeldata.mp) + { + bar(videomodes.shiftpos[i] + pmp[i][0], savedata.windowpos + pmp[i][1], player[i].ent->energy_state.mp_old, player[i].ent->modeldata.mp, &mpbarstatus); + } + } + } + + // Time box + if(!level->notime && !timeloc[4]) // Only draw if notime is set to 0 or not specified + { + spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1], videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1], HUD_Z, color_black, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1], videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z, color_black, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1], videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z, color_black, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0], savedata.windowpos + timeloc[1] + timeloc[3], videomodes.hShift + timeloc[0] + timeloc[2], savedata.windowpos + timeloc[1] + timeloc[3], HUD_Z, color_black, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] - 1, videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1, HUD_Z + 1, color_white, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] - 1, videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, color_white, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] - 1, videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, color_white, NULL); + spriteq_add_line(videomodes.hShift + timeloc[0] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, videomodes.hShift + timeloc[0] + timeloc[2] - 1, savedata.windowpos + timeloc[1] + timeloc[3] - 1, HUD_Z + 1, color_white, NULL); + } +} + +void update_loading(s_loadingbar *s, int value, int max) +{ + static uint64_t lasttick = 0; + static uint64_t soundtick = 0; + static uint64_t keybtick = 0; + int pos_x = s->bar_position.x + videomodes.hShift; + int pos_y = s->bar_position.y + videomodes.vShift; + int size_x = s->bsize; + int text_x = s->text_position.x + videomodes.hShift; + int text_y = s->text_position.y + videomodes.vShift; + uint64_t ticks = timer_gettick(); + + if(ticks - soundtick > 20) + { + sound_update_music(); + soundtick = ticks; + } + + if(ticks - keybtick > 250) + { + control_update(playercontrolpointers, 1); // Respond to exit and/or fullscreen requests from user/OS + keybtick = ticks; + } + + + if(ticks - lasttick > s->refreshMs || value < 0 || value == max) // Negative value forces a repaint. used when only bg is drawn for the first time + { + spriteq_clear(); + execute_loading_script(value, max); + if(s->set) + { + if (value < 0) + { + value = 0; + } + if(isLoadingScreenTypeBar(s->set)) + { + loadingbarstatus.size.x = size_x; + bar(pos_x, pos_y, value, max, &loadingbarstatus); + } + font_printf(text_x, text_y, s->tf, 0, Tr("Loading...")); + if(isLoadingScreenTypeBg(s->set)) + { + if(background) + { + putscreen(vscreen, background, 0, 0, NULL); + } + else + { + clearscreen(vscreen); + } + } + spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0); + video_copy_screen(vscreen); + spriteq_clear(); + } + else if(value < 0) // Original BOR v1.0029 used this method. Since loadingbg is optional, we should print this one again. + { + clearscreen(vscreen); + font_printf(120 + videomodes.hShift, 110 + videomodes.vShift, 0, 0, Tr("Loading...")); + spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0); + video_copy_screen(vscreen); + spriteq_clear(); + } + lasttick = ticks; + } +} + +/* +* Caskey, Damon V. +* 2026-08-20 +* +* Apply a signed 64-bit score adjustment to an unsigned +* player score. Saturate at zero or UINT64_MAX, award +* crossed life thresholds without an unbounded loop, and +* execute the player's score script. +*/ +void addscore(int playerindex, int64_t add) +{ + uint64_t add_magnitude = 0; + uint64_t life_awards = 0; + uint64_t old_score = 0; + uint64_t s = 0; + ScriptVariant var; // used for execute script + Script *cs; + + if(playerindex < 0) + { + return; //dont score if <0, e.g., npc damage enemy, enemy damage enemy + } + + playerindex &= 3; + + old_score = player[playerindex].score; + s = old_score; + cs = score_script + playerindex; + + if(add >= 0) + { + add_magnitude = (uint64_t)add; + + if(add_magnitude > UINT64_MAX - s) + { + s = UINT64_MAX; + } + else + { + s += add_magnitude; + } + } + else + { + add_magnitude = add == INT64_MIN + ? (uint64_t)INT64_MAX + UINT64_C(1) + : (uint64_t)-add; + + s = add_magnitude > s ? 0 : s - add_magnitude; + } + + /* + * Preserve the legacy strict threshold comparison: a + * life is awarded only after score exceeds the boundary. + */ + if(lifescore > 0 && s > old_score) + { + life_awards = ((s - 1) / lifescore) - (old_score / lifescore); + } + + if(life_awards > 0) + { + if(global_sample_list.one_up >= 0) + { + sound_play_sample(global_sample_list.one_up, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(life_awards > UINT64_MAX - player[playerindex].lives) + { + player[playerindex].lives = UINT64_MAX; + } + else + { + player[playerindex].lives += life_awards; + } + } + + player[playerindex].score = s; + + //execute a script then + if(Script_IsInitialized(cs)) + { + ScriptVariant_Init(&var); + ScriptVariant_ChangeType(&var, VT_INTEGER64); + var.llVal = add; + Script_Set_Local_Variant(cs, "score", &var); + Script_Execute(cs); + ScriptVariant_Clear(&var); + Script_Set_Local_Variant(cs, "score", &var); + } + + execute_score_script_all(playerindex, add); +} + + + + +// ---------------------------- Object handling ------------------------------ + +void free_ent(entity *e) +{ + if(!e) { + return; + } + + recursive_effect_free_collection(e); + + clear_all_scripts(e->scripts, 2); + free_all_scripts(&e->scripts); + + // Item properties. + if(e->item_properties) { + free(e->item_properties); + e->item_properties = NULL; + } + + if(e->waypoints) { + free(e->waypoints); + e->waypoints = NULL; + } + if(e->defense) { + defense_free_object(e->defense); + e->defense = NULL; + } + if(e->offense) { + offense_free_object(e->offense); + e->offense = NULL; + } + + if (e->drawmethod) { + free(e->drawmethod); + e->drawmethod = NULL; + } + + if(e->varlist) { + // Although free_ent will be only called once when the engine is shutting down, + // just clear those in case we forget something + Varlist_Clear(e->varlist); + free(e->varlist); + e->varlist = NULL; + } + free(e); + e = NULL; +} + +void free_ents() +{ + int i; + if(!ent_list) + { + return; + } + for(i = 0; i < ent_list_size; i++) + { + free_ent(ent_list[i]); + } + free(ent_list); + free(ent_stack); + ent_list = ent_stack = NULL; + ent_list_size = ent_max = ent_count = ent_stack_size = 0; +} + +entity *alloc_ent() +{ + entity *ent = malloc(sizeof(*ent)); + memset(ent, 0, sizeof(*ent)); + + ent->object_type = OBJECT_TYPE_ENTITY; + ent->defense = defense_allocate_object(); + ent->offense = offense_allocate_object(); + + ent->varlist = calloc(1, sizeof(*ent->varlist)); + // memset should be OK by know, because VT_EMPTY is zero by value, or else we should use ScriptVariant_Init + Varlist_Init(ent->varlist, max_entity_vars); + alloc_all_scripts(&ent->scripts); + return ent; +} + + + +int alloc_ents() +{ + int i; + + if(ent_list_size >= maxentities) + { + return 0; + } + + ent_list_size += MAX_ENTS; + + ent_list = realloc(ent_list, sizeof(*ent_list) * ent_list_size); + ent_stack = realloc(ent_stack, sizeof(*ent_list) * ent_list_size); + + for(i = ent_list_size - MAX_ENTS; i < ent_list_size; i++) + { + ent_list[i] = alloc_ent(); + ent_list[i]->sortid = i * 100; + ent_stack[i] = NULL; + } + //ent_count = ent_max = 0; + return 1; +} + +int is_walking(int iAni) +{ + int i; + + for(i = 0; i < max_downs; i++) + { + if (iAni == animdowns[i]) return 1; + } + for(i = 0; i < max_ups; i++) + { + if (iAni == animups[i]) return 1; + } + for(i = 0; i < max_walks; i++) + { + if (iAni == animwalks[i]) return 1; + } + for(i = 0; i < max_backwalks; i++) + { + if (iAni == animbackwalks[i]) return 1; + } + + return 0; +} + +// UT: merged DC's common walk/idle functions +// +// Caskey, Damon V. +// 2019-02-09 +// +// Rewritten for greater readability. +static bool common_anim_series(entity *ent, const animation_id_t *alterates, int max_alternates, int force_mode, animation_id_t default_animation) +{ + int i; // Loop cursor. + int loop_min; + int loop_max; + animation_id_t animation_id; // Animation to apply. + + // If we have a forced mode, we'll use it to constrict + // loop options to just the forced mode. + loop_min = force_mode ? force_mode - 1 : 0; + loop_max = force_mode ? force_mode : max_alternates; + + // Loop through available types of series animations (max idles/walks/etc.). + for (i = loop_min; i < loop_max; i++) + { + // Get animation from array of alternates. + animation_id = alterates[i]; + + // If we don't have the selected animation, just + // get out of this loop iteration. + if (!validanim(ent, animation_id)) + { + continue; + } + + // Can't be the default animation. + if (animation_id == default_animation) + { + continue; + } + + // If there's a target in range of this alternate animation, or + // we're forced to use it, switch animations. + if (force_mode || normal_find_target(animation_id, 0)) + { + ent->ducking = DUCK_NONE; + ent_set_anim(ent, animation_id, 0); + if (is_walking(animation_id)) ent->walking = 1; + + // Return true. + return 1; + } + } + + // No alternates were set. Use default if we have it. + if (validanim(ent, default_animation)) + { + ent->ducking = DUCK_NONE; + ent_set_anim(ent, default_animation, 0); + if (is_walking(default_animation)) ent->walking = 1; + + // Return true. + return 1; + } + + return 0; +} + +int common_idle_anim(entity *ent) +{ + /* + common_idle_anim + Damon Vaughn Caskey + 11012009 + Determine and set appropriate idle animation based on condition and range. + Returns 1 if any animation is set. + */ + entity *tempself = self; + + self = ent; + + self->ducking = DUCK_NONE; + if(self->idling) + { + self->idling |= IDLING_ACTIVE; + } + + if (ent->model->subtype != SUBTYPE_BIKER && ent->model->type != TYPE_NONE) // biker fix by Plombo // type none being "idle" prevented contra locked and loaded from working correctly. fixed by anallyst + { + ent->velocity.x = ent->velocity.z = 0; //Stop movement. + } + + if(validanim(ent, ANI_FAINT) && ent->energy_state.health_current <= ent->modeldata.health / 4) //ANI_FAINT and health at/below 25%? + { + ent_set_anim(ent, ANI_FAINT, 0); //Set ANI_FAINT. + goto found; //Return 1 and exit. + } + else if(validanim(ent, ANI_SLEEP) && _time > ent->sleeptime) //ANI_SLEEP, sleeptime up + { + ent_set_anim(ent, ANI_SLEEP, 0); //Set sleep anim. + goto found; //Return 1 and exit. + } + else if(validanim(ent, ANI_EDGE) && ent->edge && (ent->idling & IDLING_ACTIVE) && ent->ducking == DUCK_NONE) + { + if ( (ent->edge & EDGE_RIGHT) && (ent->edge & EDGE_LEFT) ) + { + ent_set_anim(ent, ANI_EDGE, 0); + } + else if (ent->edge & EDGE_RIGHT) + { + if (ent->direction == DIRECTION_RIGHT) + { + ent_set_anim(ent, ANI_EDGE, 0); + } + else + { + if(validanim(ent, ANI_BACKEDGE)) ent_set_anim(ent, ANI_BACKEDGE, 0); + else ent_set_anim(ent, ANI_EDGE, 0); + } + } + else if (ent->edge & EDGE_LEFT) + { + if (ent->direction == DIRECTION_LEFT) + { + ent_set_anim(ent, ANI_EDGE, 0); + } + else + { + if(validanim(ent, ANI_BACKEDGE)) ent_set_anim(ent, ANI_BACKEDGE, 0); + else ent_set_anim(ent, ANI_EDGE, 0); + } + } + goto found; //Return 1 and exit. + } + else if(common_anim_series(ent, animidles, max_idles, ent->idlemode, ANI_IDLE)) + { + goto found; + } + + self = tempself; + return 0; +found: + self = tempself; + return 1; +} + + +#define common_walk_anim(ent) \ + common_anim_series(ent, animwalks, max_walks, ent->walkmode, ANI_WALK) + +#define common_backwalk_anim(ent) \ + common_anim_series(ent, animbackwalks, max_backwalks, ent->walkmode, ANI_BACKWALK) + +#define common_up_anim(ent) \ + common_anim_series(ent, animups, max_ups, ent->walkmode, ANI_UP) + +#define common_down_anim(ent) \ + common_anim_series(ent, animdowns, max_downs, ent->walkmode, ANI_DOWN) + + +// This function find nearst x escape point from the given position +// It assumes the point is already inside the wall +static float find_nearest_wall_x(int wall, float x, float z) +{ + float x1, x2; + + x1 = level->walls[wall].x + level->walls[wall].lowerleft + (level->walls[wall].z - z) * ((level->walls[wall].upperleft - level->walls[wall].lowerleft) / level->walls[wall].depth); + x2 = level->walls[wall].x + level->walls[wall].lowerright + (level->walls[wall].z - z) * ((level->walls[wall].upperright - level->walls[wall].lowerright) / level->walls[wall].depth); + + if(diff(x1, x) > diff(x2, x)) + { + return x2; + } + else + { + return x1; + } +} + +// this method initialize an entity's A.I. behaviors +void ent_default_init(entity *e) +{ + int dodrop; + int wall; + entity *other; + + if(!e) + { + return; + } + + if((!(screen_status & IN_SCREEN_SELECT) && !_time) || e->modeldata.type != TYPE_PLAYER ) + { + if( validanim(e, ANI_SPAWN)) + { + ent_set_anim(e, ANI_SPAWN, 0); // use new playerselect spawn anim + } + else if( validanim(e, ANI_RESPAWN)) + { + ent_set_anim(e, ANI_RESPAWN, 0); + } + //else set_idle(e); + } + else if(!(screen_status & IN_SCREEN_SELECT) && _time && e->modeldata.type == TYPE_PLAYER) // mid-level respawn + { + if( validanim(e, ANI_RESPAWN)) + { + ent_set_anim(e, ANI_RESPAWN, 0); + } + else if( validanim(e, ANI_SPAWN)) + { + ent_set_anim(e, ANI_SPAWN, 0); + } + //else set_idle(e); + } + else if(screen_status & IN_SCREEN_SELECT && validanim(e, ANI_SELECT)) + { + // Play transition if we have one. Default Select otherwise. + if (validanim(e, ANI_SELECTIN)) + { + ent_set_anim(e, ANI_SELECTIN, 0); + } + else + { + ent_set_anim(e, ANI_SELECT, 0); + } + } + //else set_idle(e); + + if(!level) + { + if(!e->animation) + { + set_idle(e); + } + return; + } + + e->nograb_default = 0; // init all entities to 0 by default + e->guardpoints = e->modeldata.guardpoints; + e->jugglepoints = e->modeldata.jugglepoints; + + e->binding.object_type = OBJECT_TYPE_BIND; + + switch(e->modeldata.type) + { + case TYPE_ANY: + case TYPE_NO_COPY: + case TYPE_RESERVED: + case TYPE_UNDECLARED: + case TYPE_UNKNOWN: + //Do nothing. + break; + case TYPE_ENDLEVEL: + case TYPE_ITEM: + e->nograb = 1; + e->nograb_default = e->nograb; + break; + + case TYPE_PLAYER: + //e->direction = (level->scrolldir != SCROLL_LEFT); + e->takedamage = player_takedamage; + e->think = player_think; + e->trymove = player_trymove; + + if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN)) + { + e->takeaction = common_spawn; + } + else if(!e->animation) + { + int player_index = (int)e->playerindex; + + if(player_index < levelsets[current_set].maxplayers && level->spawn && _time && level->spawn[player_index].y > e->position.y) + { + e->takeaction = common_drop; + e->position.y = (float)level->spawn[player_index].y; + if(validanim(e, ANI_JUMP)) + { + ent_set_anim(e, ANI_JUMP, 0); + } + } + } + if(_time && e->modeldata.makeinv) + { + // Spawn invincible code + e->invincible |= INVINCIBLE_INTANGIBLE; + e->blink = (e->modeldata.makeinv > 0); + e->invinctime = _time + ABS(e->modeldata.makeinv); + e->arrowon = 1; // Display the image above the player + } + break; + case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use + if(e->modeldata.multiple == 0) + { + e->modeldata.multiple = -1; + } + + case TYPE_ENEMY: + e->think = common_think; + if(e->modeldata.subtype == SUBTYPE_BIKER) + { + e->nograb = 1; + e->nograb_default = e->nograb; + e->attacking = ATTACKING_ACTIVE; + //e->direction = (e->position.x<0); + if(e->modeldata.speed.x) + { + e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (e->modeldata.speed.x) : (-e->modeldata.speed.x); + } + else + { + e->velocity.x = (e->direction == DIRECTION_RIGHT) ? (1.7 + randf((float)0.6)) : (-(1.7 + randf((float)0.6))); + } + e->takedamage = biker_takedamage; + e->speedmul = 2; + break; + } + // define new subtypes + else if(e->modeldata.subtype == SUBTYPE_ARROW) + { + e->energy_state.health_current = 1; + if(!e->modeldata.speed.x && !(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE)) + { + e->modeldata.speed.x = 2; // Set default speed to 2 for arrows + } + else if(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + e->modeldata.speed.x = 0; + } + if(e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR) + { + e->base = 0; + } + else + { + e->base = e->position.y; + } + e->nograb = 1; + e->nograb_default = e->nograb; + e->attacking = ATTACKING_ACTIVE; + e->takedamage = arrow_takedamage; + e->speedmul = 2; + break; + } + else + { + e->trymove = common_trymove; + // Must just be a regular enemy, set defaults accordingly + if(!e->modeldata.speed.x && !(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE)) + { + e->modeldata.speed.x = 1; + } + else if(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + e->modeldata.speed.x = 0; + } + if(e->modeldata.multiple == 0) + { + e->modeldata.multiple = 5; + } + e->takedamage = common_takedamage;//enemy_takedamage; + } + + if(e->modeldata.subtype == SUBTYPE_NOTGRAB) + { + e->nograb = 1; + e->nograb_default = e->nograb; + } + + if(validanim(e, ANI_SPAWN) || validanim(e, ANI_RESPAWN)) + { + e->takeaction = common_spawn; + } + else + { + dodrop = (e->modeldata.subtype != SUBTYPE_ARROW && level && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN)); + + if(!nodropspawn && (dodrop || (e->position.x > advancex - 30 && e->position.x < advancex + videomodes.hRes + 30 && e->position.y == 0)) ) + { + e->position.y += videomodes.vRes + randf(40); + } + if(inair(e)) + { + e->takeaction = common_drop;//enemy_drop; + if(validanim(e, ANI_JUMP)) + { + ent_set_anim(e, ANI_JUMP, 0); + } + } + } + break; + // define trap type + case TYPE_TRAP: + e->think = trap_think; + e->takedamage = common_takedamage;//enemy_takedamage; + e->speedmul = 2; + break; + case TYPE_OBSTACLE: + e->nograb = 1; + e->nograb_default = e->nograb; + if(e->energy_state.health_current <= 0) + { + e->death_state |= DEATH_STATE_DEAD; // so it won't get hit + } + e->takedamage = obstacle_takedamage;//obstacle_takedamage; + break; + case TYPE_STEAMER: + e->nograb = 1; + e->nograb_default = e->nograb; + e->think = steamer_think; + e->base = e->position.y; + break; + case TYPE_TEXTBOX: // New type for displaying text purposes + e->nograb = 1; + e->nograb_default = e->nograb; + e->think = text_think; + break; + case TYPE_PROJECTILE: + + e->energy_state.health_current = 1; + e->nograb = 1; + e->nograb_default = e->nograb; + e->think = common_think; + e->takedamage = arrow_takedamage; + e->attacking = ATTACKING_ACTIVE; + + if (e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR) + { + e->base = 0; + } + else + { + e->base = e->position.y; + } + e->speedmul = 2; + e->trymove = common_trymove; + break; + + case TYPE_SHOT: + e->energy_state.health_current = 1; + e->nograb = 1; + e->nograb_default = e->nograb; + e->think = common_think; + e->takedamage = arrow_takedamage; + e->attacking = ATTACKING_ACTIVE; + if(!e->model->speed.x && !(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE)) + { + e->modeldata.speed.x = 2; // Set default speed to 2 for arrows + } + else if(e->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + e->modeldata.speed.x = 0; + } + if(e->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR) + { + e->base = 0; + } + else + { + e->base = e->position.y; + } + e->speedmul = 2; + break; + case TYPE_NONE: + e->nograb = 1; + e->nograb_default = e->nograb; + + //e->base=e->position.y; //complained? + //e->modeldata.move_config_flags |= (MOVE_CONFIG_NO_ADJUST_BASE | MOVE_CONFIG_SUBJECT_TO_GRAVITY); + + if(validanim(e, ANI_WALK)) + { + if(e->direction == DIRECTION_RIGHT) + { + e->velocity.x = e->modeldata.speed.x; + } + else + { + e->velocity.x = -(e->modeldata.speed.x); + } + e->think = anything_walk; + + common_walk_anim(e); + //ent_set_anim(e, ANI_WALK, 0); + } + else + { + e->speedmul = 2; + } + e->trymove = common_trymove; + break; + case TYPE_PANEL: + e->nograb = 1; + e->nograb_default = e->nograb; + break; + } + + /* Faction data. */ + faction_copy_data(&e->faction, &e->modeldata.faction); + + if(!e->animation) + { + set_idle(e); + } + + if(e->modeldata.multiple < 0) + { + e->modeldata.multiple = 0; + } + + if(e->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM && (other = check_platform_below(e->position.x, e->position.z, e->position.y, e))) + { + e->base = other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]; + } + else if(e->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_WALL && (wall = checkwall_below(e->position.x, e->position.z, T_MAX_CHECK_ALTITUDE)) >= 0) + { + if(level->walls[wall].height > MAX_WALL_HEIGHT) + { + e->position.x = find_nearest_wall_x(wall, e->position.x, e->position.z); + } + else + { + e->base = level->walls[wall].height; + } + } +} + +void ent_spawn_ent(entity *ent) +{ + entity *s_ent = NULL; + s_sub_entity *spawnframe = ent->animation->sub_entity_spawn; + float dy = level ? 4.0 : 0.0; + + // spawn point relative to current entity + if(spawnframe->placement == SUB_ENTITY_PLACEMENT_PARENT) + { + s_ent = spawn(ent->position.x + ((ent->direction == DIRECTION_RIGHT) ? spawnframe->position.x : -spawnframe->position.x), ent->position.z + spawnframe->position.z, ent->position.y + spawnframe->position.y, ent->direction, NULL, ent->animation->sub_entity_model_index, NULL); + } + //relative to screen position + else if(spawnframe->placement == SUB_ENTITY_PLACEMENT_SCREEN) + { + if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN)) + { + s_ent = spawn(advancex + spawnframe->position.x, advancey + spawnframe->position.z + dy, spawnframe->position.y, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + else + { + s_ent = spawn(advancex + spawnframe->position.x, spawnframe->position.z + dy, spawnframe->position.y, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + } + //absolute position in level + else + { + s_ent = spawn(spawnframe->position.x, spawnframe->position.z, spawnframe->position.y + 0.001, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + + if(s_ent) + { + //ent_default_init(s_ent); + if(s_ent->modeldata.type & TYPE_SHOT) + { + s_ent->playerindex = ent->playerindex; + } + if(s_ent->modeldata.subtype == SUBTYPE_ARROW) + { + s_ent->owner = ent; + } + s_ent->spawntype = SPAWN_TYPE_CMD_SPAWN; + s_ent->parent = ent; //maybe used by A.I. + execute_onspawn_script(s_ent); + } + +} + +void ent_summon_ent(entity *ent) +{ + entity *s_ent = NULL; + s_sub_entity *spawnframe = ent->animation->sub_entity_summon; + float dy = level ? 4.0 : 0.0; + // spawn point relative to current entity + if(spawnframe->placement == SUB_ENTITY_PLACEMENT_PARENT) + { + s_ent = spawn(ent->position.x + ((ent->direction == DIRECTION_RIGHT) ? spawnframe->position.x : -spawnframe->position.x), ent->position.z + spawnframe->position.z, ent->position.y + spawnframe->position.y, ent->direction, NULL, ent->animation->sub_entity_model_index, NULL); + } + //relative to screen position + else if(spawnframe->placement == SUB_ENTITY_PLACEMENT_SCREEN) + { + if(level && !(level->scrolldir & SCROLL_UP) && !(level->scrolldir & SCROLL_DOWN)) + { + s_ent = spawn(advancex + spawnframe->position.x, advancey + spawnframe->position.z + dy, spawnframe->position.y, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + else + { + s_ent = spawn(advancex + spawnframe->position.x, spawnframe->position.z + dy, spawnframe->position.y, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + } + //absolute position in level + else + { + s_ent = spawn(spawnframe->position.x, spawnframe->position.z, spawnframe->position.y, 0, NULL, ent->animation->sub_entity_model_index, NULL); + } + + if(s_ent) + { + if(spawnframe->placement == SUB_ENTITY_PLACEMENT_PARENT) + { + s_ent->direction = ent->direction; + } + //ent_default_init(s_ent); + if(s_ent->modeldata.type & TYPE_SHOT) + { + s_ent->playerindex = ent->playerindex; + } + if(s_ent->modeldata.subtype == SUBTYPE_ARROW) + { + s_ent->owner = ent; + } + //maybe used by A.I. + s_ent->spawntype = SPAWN_TYPE_CMD_SUMMON; + s_ent->parent = ent; + ent->subentity = s_ent; + execute_onspawn_script(s_ent); + } +} + +/* +* Caskey, Damon V. +* 2026-08-06 +* +* Add a duration to an animation timestamp without entering +* the bit-63 behavior range. An infinite operand propagates +* the canonical infinite flag. Finite overflow stops at the +* largest timestamp below that flag. +*/ +static uint64_t animation_timestamp_add_bounded( + const uint64_t left, + const uint64_t right +) { + if((left & DELAY_FLAG_INFINITE) + || (right & DELAY_FLAG_INFINITE)) { + return DELAY_INFINITE; + } + + if(left > DELAY_TIMESTAMP_MAX + || right > DELAY_TIMESTAMP_MAX + || right > DELAY_TIMESTAMP_MAX - left) { + return DELAY_TIMESTAMP_MAX; + } + + return left + right; +} + +/* +* Convert an encoded frame delay into its absolute next-frame +* timestamp. Behavior bits do not enter clock arithmetic. +*/ +static uint64_t animation_timestamp_from_delay( + const uint64_t current_time, + const uint64_t encoded_delay +) { + if(encoded_delay & DELAY_FLAG_INFINITE) { + return DELAY_INFINITE; + } + + return animation_timestamp_add_bounded( + current_time, + encoded_delay & DELAY_VALUE_MASK + ); +} + +/* +* Multiply a finite delay without exceeding the reserved +* 32-bit range or creating DELAY_INFINITE by overflow. +*/ +static uint64_t delay_multiply_bounded( + const uint64_t left, + const uint64_t right +) { + if(!left || !right) { + return 0; + } + + if(left > DELAY_FINITE_MAX + || right > DELAY_FINITE_MAX + || right > DELAY_FINITE_MAX / left) { + return DELAY_FINITE_MAX; + } + + return left * right; +} + +/* +* Caskey, Damon V. +* Unknown date (~2008) +* +* Get final delay. Revised 2026-08-06 for unsigned +* 64-bit encoded delays, preserved behavior flags, and +* bounded enhanced-delay arithmetic. +*/ +uint64_t calculate_edelay(const entity* const acting_entity, const uint64_t frame) { + const s_anim* const animation = acting_entity->animation; + const s_edelay* const edelay = &acting_entity->modeldata.edelay; + + const uint64_t encoded_delay = animation->delay[frame]; + + const uint64_t behavior_flags = + encoded_delay & DELAY_BEHAVIOR_MASK; + + const uint64_t delay = + encoded_delay & DELAY_VALUE_MASK; + + long double result; + + /* + * Return delay as-is if it falls outside + * of the range defined in the entity's + * edelay structure. This allows for + * certain frames to be exempt from the + * edelay calculations. + */ + if(edelay->range.max < 0 + || (edelay->range.min > 0 + && delay < (uint64_t)edelay->range.min) + || delay > (uint64_t)edelay->range.max) { + return behavior_flags | delay; + } + + /* + * Apply percentage and static delay + * modifier. + */ + result = (long double)delay + * (long double)edelay->factor + + (long double)edelay->modifier; + + /* + * Preserve the unmodified delay if a script supplied + * a non-number multiplier. + */ + if(result != result) { + return behavior_flags | delay; + } + + /* + * Cap results. + */ + + if(result < (long double)edelay->cap.min) { + result = (long double)edelay->cap.min; + } + + if(result > (long double)edelay->cap.max) { + result = (long double)edelay->cap.max; + } + + /* + * Edelay configuration remains signed so negative + * modifiers and caps retain their legacy meaning. + * Final value bits cannot leave the finite 32-bit + * range. Behavior bits remain unchanged. + */ + if(result <= 0.0L) { + return behavior_flags; + } + + if(result >= (long double)DELAY_FINITE_MAX) { + return behavior_flags | DELAY_FINITE_MAX; + } + + return behavior_flags | (uint64_t)result; +} + +/* +* Caskey, Damon V. +* 2018-04-20 +* +* Set up jumping velocity for an animation +* that has jump frame defined and is at the +* designated jump frame. Also spawns effect +* entity if one is defined. +*/ +bool check_jumpframe(entity * const acting_entity, const uint64_t frame) +{ + if (!acting_entity || !acting_entity->animation) + { + return 0; + } + + const s_onframe_move* jump = &acting_entity->animation->jumpframe; + + // Must have jump frame allocated. + if(jump->frame == FRAME_NONE) + { + return 0; + } + + // Must be on assigned jump frame. + if(jump->frame != frame) + { + return 0; + } + + // Chuck entity into the air. + toss(acting_entity, jump->velocity.y); + + // Set left or right horizontal velocity depending on + // current direction. + if(acting_entity->direction == DIRECTION_RIGHT) + { + acting_entity->velocity.x = jump->velocity.x; + } + else + { + acting_entity->velocity.x = -jump->velocity.x; + } + + // Lateral velocity. + acting_entity->velocity.z = jump->velocity.z; + + // Spawn an effect entity if defined. + if(jump->model_index != MODEL_INDEX_NONE) + { + entity* const effect_entity = spawn(acting_entity->position.x, acting_entity->position.z, acting_entity->position.y, acting_entity->direction, NULL, jump->model_index, NULL); + + if(effect_entity) + { + effect_entity->spawntype = SPAWN_TYPE_DUST_JUMP; + effect_entity->base = acting_entity->position.y; + effect_entity->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(effect_entity); + } + } + + return 1; + +} + +// move here to prevent some duplicated code in ent_sent_anim and update_ents +void update_frame(entity *ent, uint64_t f) +{ + entity *tempself; + s_attack attack = emptyattack; + s_axis_principal_float move; + s_anim *anim = ent->animation; + + if(f >= anim->numframes) // prevent a crash with invalid frame index. + { + return; + } + + //important! + tempself = self; + self = ent; + + self->animpos = f; + //self->currentsprite = self->animation->sprite[f]; + + if(self->animating) + { + self->nextanim = animation_timestamp_from_delay( + _time, + calculate_edelay(self, f) + ); + + self->pausetime = 0; + execute_animation_script(self); + } + + if(ent->animation != anim || ent->animpos != f) + { + goto uf_interrupted; + } + + if(level && (anim->move[f]->axis.x || anim->move[f]->axis.z)) + { + move.x = (float)(anim->move[f]->axis.x ? anim->move[f]->axis.x : 0); + move.z = (float)(anim->move[f]->axis.z ? anim->move[f]->axis.z : 0); + if(self->direction == DIRECTION_LEFT) + { + move.x = -move.x; + } + self->movex += move.x; + self->movez += move.z; + } + + if(anim->move[0]->base && anim->move[0]->base >= 0 && self->base <= 0) + { + ent->base = (float)anim->move[0]->base; + } + else if(!anim->move[0]->base || anim->move[0]->base < 0) + { + + move.y = (float)(anim->move[f]->axis.y ? anim->move[f]->axis.y : 0); + + self->base += move.y; + + if(move.y != 0) + { + self->altbase += move.y; + } + else + { + self->altbase = 0; + } + } + + if(anim->flipframe == f) + { + self->direction = !self->direction; + } + + if(anim->weaponframe && anim->weaponframe[0] == f) + { + dropweapon(self, 2); + set_weapon(self, anim->weaponframe[1], 0); + if(!anim->weaponframe[2]) + { + set_idle(self); + } + } + + if(anim->quakeframe.framestart + anim->quakeframe.cnt == f) + { + if(level) + { + if(anim->quakeframe.cnt % 2 || anim->quakeframe.v > 0) + { + level->quake = anim->quakeframe.v; + } + else + { + level->quake = anim->quakeframe.v * -1; + } + } + if((anim->quakeframe.repeat - anim->quakeframe.cnt) > 1) + { + anim->quakeframe.cnt++; + } + else + { + anim->quakeframe.cnt = 0; + } + } + + if(anim->sub_entity_unsummon == f) + { + if(self->subentity) + { + self = self->subentity; + attack.dropv = default_model_dropv; + attack.attack_force = self->energy_state.health_current; + attack.attack_type = ATK_SUB_ENTITY_UNSUMMON; + + if(self->takedamage) { + const s_defense* defense_object = defense_find_current_object(self, NULL, attack.attack_type); + self->takedamage(self, self, &attack, 0, defense_object); + + } else { + kill_entity(self, KILL_ENTITY_TRIGGER_UNSUMMON); + } + self = ent; // lol ... + self->subentity = NULL; + } + } + + //spawn / summon /unsummon features + if(anim->sub_entity_spawn && anim->sub_entity_spawn->frame == f && anim->sub_entity_model_index >= 0) + { + ent_spawn_ent(self); + } + + if(anim->sub_entity_summon && anim->sub_entity_summon->frame == f && anim->sub_entity_model_index >= 0) + { + //subentity is dead + if(!self->subentity || self->subentity->death_state & DEATH_STATE_DEAD) + { + ent_summon_ent(self); + } + } + + /* Child spawn */ + if (anim->child_spawn && anim->child_spawn[f]) + { + child_spawn_execute_list(anim->child_spawn[f], ent); + } + + if(anim->sound) + { + frame_sound_execute_collection(anim->sound[f], ent->unique_id); + } + + // Perform jumping if on a jumpframe. + check_jumpframe(self, f); + + // This animation have projectile settings? + if (anim->projectile) + { + int position_x = anim->projectile->position.x; + + if (self->direction == DIRECTION_LEFT) + { + position_x = -position_x; + } + + if (anim->projectile->throwframe == f) + { + // For backward compatible thing + // throw stars in the air, hmm, strange + // custstar custknife in animation should be checked first + // then if the entity is jumping, check star first, if failed, try knife instead + // well, try knife at last, if still failed, try star, or just let if shutdown? + +#define __trystar star_spawn(self, anim->projectile) +#define __tryknife knife_spawn(self, anim->projectile) + + if (anim->projectile->knife >= 0 || anim->projectile->flash >= 0) + { + __tryknife; + } + else if (anim->projectile->star >= 0) + { + __trystar; + } + else if (self->jumping) + { + if (!__trystar) + { + __tryknife; + } + } + else if (!__tryknife) + { + __trystar; + } + self->weapon_state |= WEAPON_STATE_DEDUCT_USE; + } + + if (anim->projectile->shootframe == f) + { + knife_spawn(self, anim->projectile); + self->weapon_state |= WEAPON_STATE_DEDUCT_USE; + } + + if (anim->projectile->tossframe == f) + { + bomb_spawn(self, anim->projectile); + self->weapon_state |= WEAPON_STATE_DEDUCT_USE; + } + } + +uf_interrupted: + + //important! + self = tempself; + + #undef __trystar + #undef __tryknife +} + + +void ent_set_anim(entity *acting_entity, animation_id_t aninum, bool resetable) { + + s_anim *ani = NULL; + int animpos; + + if(!acting_entity) { + //printf("FATAL: tried to set animation with invalid address (no such object)"); + return; + } + + if(aninum >= max_animations) { + //printf("FATAL: tried to set animation with invalid index (%s, %i)", ent->name, aninum); + return; + } + + if(!validanim(acting_entity, aninum)) { + //printf("FATAL: tried to set animation with invalid address (%s, %i)", acting_entity->name, aninum); + return; + } + + ani = acting_entity->modeldata.animation[aninum]; + + /* + * If reset flag is not enabled and the entity + * is already on the requested animation, then + * just bail. + */ + if(!resetable && acting_entity->animation == ani) { + return; + } + + if(ani->numframes == 0) { + return; + } + + if(acting_entity->animation && ((resetable & 2) || (ani->sync >= 0 && acting_entity->animation->sync == ani->sync))) { + animpos = acting_entity->animpos; + + if(animpos >= ani->numframes) { + animpos = 0; + } + + acting_entity->animnum_previous = acting_entity->animnum; + acting_entity->animnum = aninum; + acting_entity->animation = ani; + acting_entity->animpos = animpos; + acting_entity->walking = false; + + } else { + acting_entity->animnum_previous = acting_entity->animnum; + acting_entity->animnum = aninum; // Stored for nocost usage + acting_entity->animation = ani; + acting_entity->animation->hit_count = 0; + + acting_entity->animating = ANIMATING_FORWARD; + acting_entity->lasthit = acting_entity->grabbing; + acting_entity->altbase = 0; + acting_entity->walking = false; + + update_frame(acting_entity, 0); + } +} + +unsigned char *model_get_colourmap(s_model *model, unsigned which) +{ + if(which <= 0 || which > model->maps_loaded) + { + return model->palette; + } + else + { + return model->colourmap[which - 1]; + } +} + +// 0 = none, 1+ = alternative +void ent_set_colourmap(entity *ent, uint64_t which) +{ + if(which > ent->modeldata.maps_loaded) + { + which = 0; + } + if(which <= 0) + { + ent->colourmap = ent->modeldata.palette; + } + else + { + ent->colourmap = ent->modeldata.colourmap[which - 1]; + } + ent->map = which; +} + +// used by ent_set_model +void ent_copy_uninit(entity *ent, s_model *oldmodel) +{ + if(ent->modeldata.multiple < 0) + { + ent->modeldata.multiple = oldmodel->multiple; + } + + ent->modeldata.move_config_flags = oldmodel->move_config_flags; + + if(ent->modeldata.aimove == AIMOVE1_NONE) + { + ent->modeldata.aimove = oldmodel->aimove; + } + if (ent->modeldata.aiattack == -1) + { + ent->modeldata.aiattack = oldmodel->aiattack; + } + + faction_copy_data(&ent->modeldata.faction, &oldmodel->faction); + faction_copy_data(&ent->faction, &oldmodel->faction); + + if(!ent->modeldata.health) + { + ent->modeldata.health = oldmodel->health; + } + if(!ent->modeldata.mp) + { + ent->modeldata.mp = oldmodel->mp; + } + if(ent->modeldata.risetime.rise == -1) + { + ent->modeldata.risetime.rise = oldmodel->risetime.rise; + } + /* + if(!ent->modeldata.grab_resistance) + ent->modeldata.grab_resistance = oldmodel->antigrab; + if(!ent->modeldata.grab_force) + ent->modeldata.grab_force = oldmodel->grabforce; + if(!ent->modeldata.paingrab) + ent->modeldata.paingrab = oldmodel->paingrab;*/ + + if(ent->energy_state.health_current > ent->modeldata.health) + { + ent->energy_state.health_current = ent->modeldata.health; + } + if(ent->energy_state.mp_current > ent->modeldata.mp) + { + ent->energy_state.mp_current = ent->modeldata.mp; + } + + ent->shadow_config_flags = ent->modeldata.shadow_config_flags; +} + + +//if syncAnim is set, only change animation reference +void ent_set_model(entity *ent, char *modelname, int syncAnim) +{ + if(ent == NULL) + { + borShutdown(1, "FATAL: tried to change model of invalid object"); + } + + s_model* m = findmodel(modelname); + + if(m == NULL) + { + borShutdown(1, "Model not found: '%s'", modelname); + } + + s_model oldmodel = ent->modeldata; + ent->model = m; + ent->modeldata = *m; + ent_copy_uninit(ent, &oldmodel); + ent_set_colourmap(ent, ent->map); + + if(syncAnim && m->animation[ent->animnum]) + { + ent->animation = m->animation[ent->animnum]; + if(ent->animpos >= ent->animation->numframes) + { + ent->animpos = ent->animation->numframes - 1; + } + + ent->nextanim = animation_timestamp_from_delay( + _time, + calculate_edelay(ent, ent->animpos) + ); + + + //update_frame(ent, ent->animpos); + } + else + { + ent->attacking = ATTACKING_NONE; + + if((!(screen_status & IN_SCREEN_SELECT) && !_time) || !(ent->modeldata.type & TYPE_PLAYER)) + { + // use new playerselect spawn anim + if( validanim(ent, ANI_SPAWN)) + { + ent_set_anim(ent, ANI_SPAWN, 0); + } + else + { + if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0); + } + } + else if(!(screen_status & IN_SCREEN_SELECT) && _time && (ent->modeldata.type & TYPE_PLAYER)) + { + // mid-level respawn + if( validanim(ent, ANI_RESPAWN)) + { + ent_set_anim(ent, ANI_RESPAWN, 0); + } + else if( validanim(ent, ANI_SPAWN)) + { + ent_set_anim(ent, ANI_SPAWN, 0); + } + else + { + if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0); + } + } + else if(screen_status & IN_SCREEN_SELECT && validanim(ent, ANI_SELECT)) + { + // Play transition if we have one. Default Select otherwise. + if (validanim(ent, ANI_SELECTIN)) + { + ent_set_anim(ent, ANI_SELECTIN, 0); + } + else + { + ent_set_anim(ent, ANI_SELECT, 0); + } + } + else + { + if( validanim(ent, ANI_IDLE)) ent_set_anim(ent, ANI_IDLE, 0); + } + } +} + +// Caskey, Damon V. +// ~2018 +// +// Allocate memory for a drawmethod and return pointer. +s_drawmethod *allocate_drawmethod() +{ + s_drawmethod *result; + + // Allocate memory and get the pointer. + result = malloc(sizeof(*result)); + + // Copy default values into new drawmethod. + memcpy(result, &plainmethod, sizeof(*result)); + + return result; +} + +// Caskey, Damon V. +// 2019-12-13 +// +// Allocate memory for a projectile animation setting and return pointer. +s_projectile* allocate_projectile() +{ + s_projectile* result; + + // Allocate memory and get the pointer. + result = malloc(sizeof(*result)); + + // Copy default values into new projectile setting. + memcpy(result, &projectile_default_config, sizeof(*result)); + + return result; +} + +// Caskey, Damon V. +// 2019-12-11 +// +// Allocate memory for a sub entity command and return pointer. +s_sub_entity *allocate_sub_entity() +{ + s_sub_entity *result; + + // Allocate memory and get the pointer. + result = malloc(sizeof(*result)); + + // Set any default values we need. + result->frame = FRAME_NONE; + + return result; +} + +/* +* Caskey, Damon V. +* 2026-08-08 +* +* Return the next process-lifetime entity instance ID. +* Zero and UINT64_MAX remain reserved for no-owner and +* wildcard selectors respectively. +*/ +static uint64_t entity_unique_id_next(void) { + entity_unique_id_counter++; + + if(entity_unique_id_counter == ENTITY_UNIQUE_ID_ALL) { + entity_unique_id_counter = UINT64_C(1); + } + + return entity_unique_id_counter; +} + +entity *spawn(const float pos_x, const float pos_z, const float pos_y, const e_direction direction, char *model_name, const int model_index, s_model* model_pointer) +{ + entity *acting_entity = NULL; + int i, id; + s_defense *dfs; + s_offense *ofs; + e_object_type object_type; + Varlist *vars; + s_scripts *scripts; + + /* + * Prioritize parameter we use to + * spawn: + * + * 1. model_pointer. + * 2. model_index. + * 3. model_name. + */ + + s_model* spawn_model = model_pointer; + + if(!spawn_model) + { + if(model_index >= 0) + { + spawn_model = model_cache[model_index].model; + } + else if(model_name) + { + spawn_model = findmodel(model_name); + } + } + + if(spawn_model == NULL) + { + return NULL; + } + + if(ent_count >= ent_list_size && !alloc_ents()) + { + return NULL; //out of memory ? + } + + for(i = 0; i < ent_list_size; i++) + { + if(!ent_list[i]->exists || (ent_count >= spawnoverride && ent_list[i]->modeldata.priority < 0 && ent_list[i]->modeldata.priority <= spawn_model->priority)) + { + acting_entity = ent_list[i]; + if(acting_entity->exists) + { + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_SPAWN_OVERRIDE); + } + // save these values, or they will loss when memset called + object_type = acting_entity->object_type; + id = acting_entity->sortid; + dfs = acting_entity->defense; + + ofs = acting_entity->offense; + vars = acting_entity->varlist; + Varlist_Cleanup(vars); + + memcpy(dfs, spawn_model->defense, sizeof(*dfs)*max_attack_types); + memcpy(ofs, spawn_model->offense, sizeof(*ofs)*max_attack_types); + + // clear up + clear_all_scripts(acting_entity->scripts, 1); + if(acting_entity->waypoints) + { + free(acting_entity->waypoints); + } + + /* Just to make the recrusive collection is free. */ + recursive_effect_free_collection(acting_entity); + + scripts = acting_entity->scripts; + memset(acting_entity, 0, sizeof(*acting_entity)); + acting_entity->unique_id = entity_unique_id_next(); + + // e->drawmethod = plainmethod; + acting_entity->drawmethod = allocate_drawmethod(); + + acting_entity->drawmethod->config &= ~DRAWMETHOD_CONFIG_ENABLED; + + // add to list and count current entities + acting_entity->exists = 1; + ent_count++; + + acting_entity->modeldata = *spawn_model; // copy the entir model data here + acting_entity->model = spawn_model; + acting_entity->defaultmodel = spawn_model; + + acting_entity->scripts = scripts; + // copy from model a fresh script + + copy_all_scripts(spawn_model->scripts, acting_entity->scripts, 1); + + if(ent_count > ent_max) + { + ent_max = ent_count; + } + acting_entity->timestamp = _time; // log time so update function will ignore it if it is new + + acting_entity->energy_state.health_current = acting_entity->modeldata.health; + acting_entity->energy_state.mp_current = acting_entity->modeldata.mp; + acting_entity->knockdowncount = acting_entity->modeldata.knockdowncount; + acting_entity->position.x = pos_x; + acting_entity->position.z = pos_z; + acting_entity->position.y = pos_y; + acting_entity->shadow_config_flags = acting_entity->modeldata.shadow_config_flags; + acting_entity->direction = direction; + acting_entity->nextthink = _time + 1; + acting_entity->nextmove = _time + 1; + acting_entity->speedmul = 1; + ent_set_colourmap(acting_entity, 0); + acting_entity->lifespancountdown = spawn_model->lifespan; // new life span countdown + + if((acting_entity->modeldata.type & TYPE_PLAYER) && ((level && level->nohit == DAMAGE_FROM_PLAYER_OFF) || savedata.mode)) + { + acting_entity->faction.type_hostile &= ~TYPE_PLAYER; + acting_entity->faction.type_damage_direct &= ~TYPE_PLAYER; + } + + if(acting_entity->modeldata.type & TYPE_PLAYER) + { + acting_entity->playerindex = currentspawnplayer; + } + + if(acting_entity->modeldata.type & TYPE_TEXTBOX) + { + textbox = acting_entity; + } + + strncpy(acting_entity->name, acting_entity->modeldata.name, MAX_NAME_LEN - 1); + // copy back the value + acting_entity->object_type = object_type; + acting_entity->sortid = id; + acting_entity->defense = dfs; + acting_entity->offense = ofs; + acting_entity->varlist = vars; + + ent_default_init(acting_entity); + return acting_entity; + } + } + return NULL; +} + + + +// Break the link an entity has with another one +void ent_unlink(entity *e) +{ + if(e->link) + { + e->link->link = NULL; + e->link->grabbing = NULL; + } + e->link = NULL; + e->grabbing = NULL; +} + + + +// Link two entities together +void ents_link(entity *e1, entity *e2) +{ + ent_unlink(e1); + ent_unlink(e2); + e1->grabbing = e2; // Added for platform layering + e1->link = e2; + e2->link = e1; +} + + + +void kill_entity(entity *victim, e_kill_entity_trigger trigger) +{ + int i = 0; + s_attack attack = emptyattack; + entity *tempent = self; + + if(victim == NULL || !victim->exists) { + return; + } + + execute_onkill_script(victim, trigger); + + recursive_effect_free_collection(victim); + + ent_unlink(victim); + victim->weapent = NULL; + victim->energy_state.health_current = 0; + victim->exists = 0; + ent_count--; + + //UT: caution, script function killentity calls this + clear_all_scripts(victim->scripts, 1); + + if(victim->parent && victim->parent->subentity == victim) { + victim->parent->subentity = NULL; + } + + victim->parent = NULL; + + if(victim->modeldata.summonkill) { + attack.attack_type = ATK_SUB_ENTITY_PARENT_KILL; + attack.dropv = default_model_dropv; + } + + const s_defense* defense_object = defense_find_current_object(self, NULL, attack.attack_type); + + // kill minions + if(victim->modeldata.summonkill == 1 && victim->subentity) { + // kill only summoned one + victim->subentity->parent = NULL; + self = victim->subentity; + attack.attack_force = self->energy_state.health_current; + + if(self->takedamage && !level_completed) { + self->takedamage(self, self, &attack, 0, defense_object); + + } else { + kill_entity(self, KILL_ENTITY_TRIGGER_PARENT_KILL_SUMMON); + } + } + + victim->subentity = NULL; + + for (i = 0; i < MAX_PLAYERS; i++) { + if(victim == player[i].ent) { + player[i].ent = NULL; + break; + } + } + + if(victim == smartbomber) { + smartbomber = NULL; + } + + if(victim == textbox) { + textbox = NULL; + } + + for(i = 0; i < ent_max; i++) { + if(ent_list[i]->exists) { + // kill all minions + self = ent_list[i]; + if(self->parent == victim) { + self->parent = NULL; + if(victim->modeldata.summonkill == 2) { + + attack.attack_force = self->energy_state.health_current; + + if(self->takedamage && !level_completed) { + self->takedamage(self, self, &attack, 0, defense_object); + } else { + kill_entity(self, KILL_ENTITY_TRIGGER_PARENT_KILL_ALL); + } + } + } + + if(self->owner == victim) { + self->owner = victim->owner; + } + + if(self->opponent == victim) { + self->opponent = NULL; + } + + if(self->binding.target == victim) { + self->binding.target = NULL; + } + + if(self->landed_on_platform == victim) { + self->landed_on_platform = NULL; + } + + if(self->hithead == victim) { + self->hithead = NULL; + } + + if(self->lasthit == victim) { + self->lasthit = NULL; + } + + if(!textbox && (self->modeldata.type & TYPE_TEXTBOX)) { + textbox = self; + } + } + } + + victim = NULL; + + //free_ent(victim); + //victim = alloc_ent(); + + self = tempent; +} + + +void kill_all(){ + + int i; + entity *e = NULL; + + for(i = 0; i < ent_max; i++) { + e = ent_list[i]; + if (e && e->exists) { + execute_onkill_script(e, KILL_ENTITY_TRIGGER_ALL); + + recursive_effect_free_collection(e); + ent_unlink(e); + + clear_all_scripts(e->scripts, 1); + } + e->exists = 0; // well, no need to use kill function + } + textbox = smartbomber = NULL; + _time = 0; + ent_count = ent_max = ent_stack_size = 0; + if(ent_list_size > MAX_ENTS) {//shrinking... + free_ents(); + alloc_ents(); //this shouldn't return 0, because the list shrinks... + } +} + +/* +* Caskey, Damon V. +* 2022-04-20 +* +* Replacement for legacy canbegrabbed +* macro. Return true if target entity +* is eligible for grabbing. +*/ +int check_canbegrabbed(entity* acting_entity, entity* target_entity) +{ + if (!target_entity->animation->vulnerable[target_entity->animpos]) + { + return 0; + } + + /* + * Note move refers to "move animation + * commands, not if entity has velocity. + */ + + if (acting_entity->animation->move) + { + if (acting_entity->animation->move[acting_entity->animpos]->axis.x != 0) + { + return 0; + } + + if (acting_entity->animation->move[acting_entity->animpos]->axis.z != 0) + { + return 0; + } + } + + if (target_entity->nograb) + { + return 0; + } + + if (target_entity->invincible & INVINCIBLE_INTANGIBLE) + { + return 0; + } + + if (target_entity->link) + { + return 0; + } + + if (target_entity->model->weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + return 0; + } + + if (inair(target_entity)) + { + return 0; + } + + /* + * DC 2022-04-20. + * + * This check seems very open-ended. I would + * think savedata.mode should equal something + * specific and not just "true". Will need to + * look over the menu options code to make + * sure this is behaving as intended. + */ + + if (acting_entity->modeldata.type & TYPE_PLAYER && target_entity->modeldata.type & TYPE_PLAYER) + { + if (savedata.mode) + { + return 0; + } + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2022-04-09 +* +* Replacement for legacy cangrab +* macro. Return true if acting +* entity can grab target entity. +*/ +int check_cangrab(entity* acting_entity, entity* target_entity) +{ + int grab_resistance = 0; + + if (!check_canbegrabbed(acting_entity, target_entity)) + { + return 0; + } + + if (inair_range(acting_entity)) + { + return 0; + } + + if (diff(target_entity->position.y, acting_entity->position.y) > T_WALKOFF) + { + return 0; + } + + /* + * Grab resistance. Acting entity's grab force + * must exceed target's total grab resistance. + * + * If the target as paingrab property, we add + * its value to grab resistance property if + * the target's pain flag is not active. + */ + + grab_resistance = target_entity->modeldata.grab_resistance; + + if (target_entity->modeldata.paingrab) + { + if ((target_entity->inpain & IN_PAIN_HIT) == 0) + { + grab_resistance += target_entity->modeldata.paingrab; + } + } + + if (grab_resistance > acting_entity->modeldata.grab_force) + { + return 0; + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2020-02-04 +* +* Test collision between two boxes. If any overlap is found, +* populates collision_check_data->return_overlap with overlap +* position and returns true. +*/ +int check_collision(s_collision_check_data* collision_data) +{ + s_box seek_pos; + s_box detect_pos; + + /* + * X axis. + * + * Before we can check X positions, we need to + * accomidate handle left/right flipping of + * both the seeker and target. + */ + + if (collision_data->seeker_direction == DIRECTION_LEFT) + { + seek_pos.left = collision_data->seeker_pos->x - collision_data->seeker_coords->width; + seek_pos.right = collision_data->seeker_pos->x - collision_data->seeker_coords->x; + } + else + { + seek_pos.left = collision_data->seeker_pos->x + collision_data->seeker_coords->x; + seek_pos.right = collision_data->seeker_pos->x + collision_data->seeker_coords->width; + } + + if (collision_data->target_direction == DIRECTION_LEFT) + { + detect_pos.left = collision_data->target_pos->x - collision_data->target_coords->width; + detect_pos.right = collision_data->target_pos->x - collision_data->target_coords->x; + } + else + { + detect_pos.left = collision_data->target_pos->x + collision_data->target_coords->x; + detect_pos.right = collision_data->target_pos->x + collision_data->target_coords->width; + } + + // If we are out of bounds, there's no collision. + if (seek_pos.left > detect_pos.right || seek_pos.right < detect_pos.left) + { + return 0; + } + + /* + * Y axis. + * + * This looks backwards, but we're not crazy. + * + * The text input treats box as starting from + * a Y position with a Y size that proceeds + * downward, but when checking for collision + * we do the opposite. So here the Y position + * is our lower coordinate and Y size is the + * top coordinate. + */ + + seek_pos.bottom = collision_data->seeker_pos->y + -(collision_data->seeker_coords->height); + seek_pos.top = collision_data->seeker_pos->y + - (collision_data->seeker_coords->y); + + detect_pos.bottom = collision_data->target_pos->y + -(collision_data->target_coords->height); + detect_pos.top = collision_data->target_pos->y + - (collision_data->target_coords->y); + + // If we are out of bounds, there's no collision. + if (seek_pos.bottom > detect_pos.top || seek_pos.top < detect_pos.bottom) + { + return 0; + } + + /* + * Z axis. + * + * Same principal as the left and right sides of X + * axis, except we don't have to worry about flipping + * direction. + */ + + seek_pos.background = collision_data->seeker_pos->z - collision_data->seeker_coords->z_background; + seek_pos.foreground = collision_data->seeker_pos->z + collision_data->seeker_coords->z_foreground; + + detect_pos.background = collision_data->target_pos->z - collision_data->target_coords->z_background; + detect_pos.foreground = collision_data->target_pos->z + collision_data->target_coords->z_foreground; + + // If we are out of bounds, there's no collision. + if (seek_pos.background > detect_pos.foreground || seek_pos.foreground < detect_pos.background) + { + return 0; + } + + /* + * If we got this far, find the collision area and apply + * values to collision area box supplied by parent function. + * + * We do this by treating the collision area as a third + * box set of coordinates between the attack and detect + * boxes. Then we find the center of our third box. This + * gives us a calculated center of the collision detection + * point. + */ + + collision_data->return_overlap->left = seek_pos.left < detect_pos.left ? detect_pos.left : seek_pos.left; + collision_data->return_overlap->right = seek_pos.right > detect_pos.right ? detect_pos.right : seek_pos.right; + collision_data->return_overlap->center_x = (collision_data->return_overlap->left + collision_data->return_overlap->right) / 2; + + collision_data->return_overlap->bottom = seek_pos.bottom < detect_pos.bottom ? detect_pos.bottom : seek_pos.bottom; + collision_data->return_overlap->top = seek_pos.top > detect_pos.top ? detect_pos.top : seek_pos.top; + collision_data->return_overlap->center_y = (collision_data->return_overlap->top + collision_data->return_overlap->bottom) / 2; + + collision_data->return_overlap->background = seek_pos.background < detect_pos.background ? detect_pos.background : seek_pos.background; + collision_data->return_overlap->foreground = seek_pos.foreground > detect_pos.foreground ? detect_pos.foreground : seek_pos.foreground; + collision_data->return_overlap->center_z = (collision_data->return_overlap->background + collision_data->return_overlap->foreground) / 2; + + return 1; +} + +/* +* Caskey, Damon V. +* 2020-02-04 +* +* Send collision and entity data to last hit structure. +* This data is vital for checking hit reactions, spawning +* flash effects, performing hit overrides, populating +* script variables and other post hit functionality. +*/ +void populate_lasthit(const s_collision_check_data* const collision_data, s_collision_instance* const collision_attack, s_collision_instance* const detect_collision_body, s_collision_instance* const detect_collision_attack) { + + /* + * Why have both attack and collision_attack when + * attack is a member of collision_attack? + * + * There are times (like damage on landing) where + * the lasthit structure is populated manually + * and we don't have a collision container, only + * the property object. + */ + lasthit.collision_attack = collision_attack; + lasthit.attack = collision_attack ? collision_attack->attack : NULL; + + lasthit.detect_collision_body = detect_collision_body; + lasthit.detect_body = detect_collision_body ? detect_collision_body->body : NULL; + + lasthit.detect_collision_attack = detect_collision_attack; + + lasthit.position.x = collision_data->return_overlap->center_x; + lasthit.position.y = collision_data->return_overlap->center_y; + lasthit.position.z = collision_data->return_overlap->center_z; + + lasthit.target = collision_data->target_ent; + lasthit.attacker = collision_data->seeker_ent; + lasthit.confirm = 1; +} + +/* +* Caskey, Damon V. +* 2026-07-01 +* +* Check one seeking collision box against all active +* body collision boxes on the target's current frame. +* +* This replaces the old linked-list walk with an active +* mask scan. The function only visits slots marked active +* in the target frame's body collision collection. +* +* Returns: +* - Pointer to the body collision instance that was hit. +* - NULL if no body collision is found. +*/ +s_collision_instance* check_collision_vs_body(s_collision_check_data* const collision_check_data) { + s_anim* animation = NULL; + s_collision_collection* collection = NULL; + s_collision_instance* detect_collision = NULL; + uint64_t active_status; + int frame; + int collision_index; + + /* + * No collision check data means there is nothing + * meaningful to evaluate. + */ + if (!collision_check_data) { + return NULL; + } + + animation = collision_check_data->target_animation; + frame = collision_check_data->target_frame; + + /* + * Guard the animation and frame table lookup. + */ + if (!animation || frame < 0 || frame >= animation->numframes) { + return NULL; + } + + /* + * If the animation has no body collision frame + * table, then there are no body boxes to check. + */ + if (!animation->collision_body) { + return NULL; + } + + /* + * Get the body collision collection for the requested + * target frame. + */ + collection = animation->collision_body[frame]; + + /* + * No collection, or no active slots in the collection, + * means there are no target body boxes on this frame. + */ + if (!collection || !collection->active_status) { + return NULL; + } + + /* + * Work from a local copy of the active mask. + * + * The frame collection itself must not be modified by + * collision checks. Each loop consumes one active bit + * from this local copy. + */ + active_status = collection->active_status; + + while (active_status) { + + /* + * Get the lowest active slot index from the local + * active mask copy. + */ + collision_index = collision_get_lowest_active_index(active_status); + + /* + * Clear the lowest active bit so the next loop + * moves to the next active body slot. + */ + active_status &= active_status - 1; + + /* + * Active mask and pointer slots should agree, but + * guard against damaged or script-modified data. + */ + detect_collision = collection->slots[collision_index]; + + if (!detect_collision) { + continue; + } + + /* + * Empty coordinates mean this slot does not have + * a usable collision box for this frame. + */ + if (!collision_check_has_coords(&detect_collision->coords)) { + continue; + } + + /* + * Populate the target coordinate pointer and run + * the shared collision test. + * + * Coordinates live inline on the collision instance, + * so pass the address of the embedded coords member. + */ + collision_check_data->target_coords = &detect_collision->coords; + + if (check_collision(collision_check_data)) { + return detect_collision; + } + } + + /* + * No active target body slot collided with the seeker. + */ + return NULL; +} + +/* +* Caskey, Damon V. +* 2026-07-01 +* +* Check one seeking collision box against all active +* attack collision boxes on the target's current frame. +* +* This replaces the old linked-list walk with an active +* mask scan. The function only visits slots marked active +* in the target frame's attack collision collection. +* +* Returns: +* - Pointer to the attack collision instance that was hit. +* - NULL if no attack collision is found. +*/ +s_collision_instance* check_collision_vs_attack(s_collision_check_data* const collision_check_data) { + s_anim* animation = NULL; + s_collision_collection* collection = NULL; + s_collision_instance* detect_collision = NULL; + uint64_t active_status; + int frame; + int collision_index; + + /* + * No collision check data means there is nothing + * meaningful to evaluate. + */ + if (!collision_check_data) { + return NULL; + } + + animation = collision_check_data->target_animation; + frame = collision_check_data->target_frame; + + /* + * Guard the animation and frame table lookup. + */ + if (!animation || frame < 0 || frame >= animation->numframes) { + return NULL; + } + + /* + * If the animation has no attack collision + * frame table, then there are no attack boxes + * to check. + */ + if (!animation->collision_attack) { + return NULL; + } + + /* + * Get the attack collision collection for the requested + * target frame. + */ + collection = animation->collision_attack[frame]; + + /* + * No collection, or no active slots in the collection, + * means there are no target attack boxes on this frame. + */ + if (!collection || !collection->active_status) { + return NULL; + } + + /* + * Work from a local copy of the active mask. + * + * The frame collection itself must not be modified by + * collision checks. Each loop consumes one active bit + * from this local copy. + */ + active_status = collection->active_status; + + while (active_status) { + + /* + * Get the lowest active slot index from the local + * active mask copy. + */ + collision_index = collision_get_lowest_active_index(active_status); + + /* + * Clear the lowest active bit so the next loop + * moves to the next active attack slot. + */ + active_status &= active_status - 1; + + /* + * Active mask and pointer slots should agree, but + * guard against damaged or script-modified data. + */ + detect_collision = collection->slots[collision_index]; + + if (!detect_collision) { + continue; + } + + /* + * Empty coordinates mean this slot does not have + * a usable collision box for this frame. + */ + if (!collision_check_has_coords(&detect_collision->coords)) { + continue; + } + + /* + * An attack collision scan should only report + * instances that actually carry attack data. + */ + if (!detect_collision->attack) { + continue; + } + + /* + * Populate the target coordinate pointer and run + * the shared collision test. + * + * Coordinates live inline on the collision instance, + * so pass the address of the embedded coords member. + */ + collision_check_data->target_coords = &detect_collision->coords; + + if (check_collision(collision_check_data)) { + return detect_collision; + } + } + + /* + * No active target attack slot collided with the seeker. + */ + return NULL; +} + +/* +* Caskey, Damon V. +* 2026-07-02 +* +* Check whether an attacker's active attack collision +* boxes hit a target's active body collision boxes. +* +* This replaces the old linked-list walk with an active +* mask scan over the attacker's current frame attack +* collision collection. +*/ +int checkhit(entity* attacker, entity* target) { + s_collision_collection* seek_collection = NULL; + s_collision_instance* seek_collision = NULL; + s_collision_instance* detect_collision_attack = NULL; + s_collision_instance* detect_collision_body = NULL; + s_collision_check_data collision_check_data; + s_axis_principal_int seeker_pos; + s_axis_principal_int target_pos; + s_box return_overlap; + uint64_t active_status; + int collision_index; + + int attacker_frame; + int target_frame; + + /* + * Basic guards. + * + * Do not touch animation frame tables until both + * animation pointers and frame indexes are valid. + */ + if (!attacker + || !target + || attacker == target + || !attacker->animation + || !target->animation) { + return FALSE; + } + + attacker_frame = attacker->animpos; + target_frame = target->animpos; + + /* + * Guard animation frame table lookups. + */ + if (attacker_frame < 0 + || attacker_frame >= attacker->animation->numframes + || target_frame < 0 + || target_frame >= target->animation->numframes) { + return FALSE; + } + + /* + * Now it is safe to inspect per-frame animation tables. + */ + if (!attacker->animation->collision_attack + || !target->animation->collision_body + || !target->animation->vulnerable + || !target->animation->vulnerable[target_frame]) { + return FALSE; + } + + /* + * Get the attacker's attack collision collection for + * the current animation frame. + */ + seek_collection = attacker->animation->collision_attack[attacker_frame]; + + /* + * No collection, or no active slots, means the attacker + * has no attack boxes to test on this frame. + */ + if (!seek_collection || !seek_collection->active_status) { + return FALSE; + } + + /* + * We'll use these in collision check data structure + * in lieu of memory allocations. + */ + seeker_pos.x = (int)attacker->position.x; + seeker_pos.y = (int)attacker->position.y; + seeker_pos.z = (int)attacker->position.z; + + target_pos.x = (int)target->position.x; + target_pos.y = (int)target->position.y; + target_pos.z = (int)target->position.z; + + /* + * Populate collision check data with everything + * that does not change during the active-slot scan. + */ + collision_check_data.seeker_pos = &seeker_pos; + collision_check_data.target_pos = &target_pos; + collision_check_data.return_overlap = &return_overlap; + + collision_check_data.seeker_ent = attacker; + collision_check_data.target_ent = target; + collision_check_data.target_animation = target->animation; + collision_check_data.target_frame = target_frame; + + collision_check_data.seeker_direction = attacker->direction; + collision_check_data.target_direction = target->direction; + + /* + * Work from a local copy of the active mask. + * + * Collision checks must not modify the animation frame's + * collision collection. Each loop consumes one active bit + * from this local mask copy. + */ + active_status = seek_collection->active_status; + + while (active_status) { + + /* + * Get the lowest active attack slot index from the + * local active mask copy. + */ + collision_index = collision_get_lowest_active_index(active_status); + + /* + * Clear the lowest active bit so the next loop + * moves to the next active attack slot. + */ + active_status &= active_status - 1; + + /* + * Active mask and pointer slots should agree, but + * guard against damaged or script-modified data. + */ + seek_collision = seek_collection->slots[collision_index]; + + if (!seek_collision) { + continue; + } + + /* + * The seeker side must have both usable coordinates + * and an attack property. Coordinates live inline on + * the collision instance. + */ + if (!seek_collision->attack + || !collision_check_has_coords(&seek_collision->coords)) { + continue; + } + + /* + * Populate the seeker coordinate pointer for this + * active attack slot. + */ + collision_check_data.seeker_coords = &seek_collision->coords; + + /* + * Reset per-slot detection outputs before checking. + */ + detect_collision_attack = NULL; + detect_collision_body = NULL; + + /* + * Check this attack box against the target's active + * body boxes. + */ + detect_collision_body = check_collision_vs_body(&collision_check_data); + + if (detect_collision_body) { + populate_lasthit( + &collision_check_data, + seek_collision, + detect_collision_body, + detect_collision_attack); + + return TRUE; + } + + /* + * Counterattack collision checks are disabled + * pending a rewrite of the old linked-list walk + * to the new active-mask scan pattern. + * + * If/when this path is restored, check_collision_vs_attack() + * now returns s_collision_instance* and already uses the + * active-mask scan pattern. + */ + /* + detect_collision_attack = check_collision_vs_attack(&collision_check_data); + + if (detect_collision_attack) { + populate_lasthit( + &collision_check_data, + seek_collision, + detect_collision_body, + detect_collision_attack); + + return TRUE; + } + */ + } + + /* + * No active attacker attack slot collided with any + * active target collision slot. + */ + return FALSE; +} + + +/* +* Calculates the coef relative to the bottom left +* point. This is done by figuring out how far the +* entity is from the bottom of the platform and +* multiplying the result by the difference of the +* bottom left point and the top left point divided +* by depth of the platform. The same is done for +* the right side, and checks to see if they are +* within the bottom/top and the left/right area. +*/ +int testhole(int hole, float x, float z) +{ + float coef1, coef2; + if(z <= level->holes[hole].z && z >= level->holes[hole].z - level->holes[hole].depth) + { + coef1 = (level->holes[hole].z - z) * ((level->holes[hole].upperleft - level->holes[hole].lowerleft) / level->holes[hole].depth); + coef2 = (level->holes[hole].z - z) * ((level->holes[hole].upperright - level->holes[hole].lowerright) / level->holes[hole].depth); + if(x >= level->holes[hole].x + level->holes[hole].lowerleft + coef1 && x <= level->holes[hole].x + level->holes[hole].lowerright + coef2) + { + return 1; + } + } + return 0; +} + +// find all holes here and return the count +int checkholes(float x, float z) +{ + int i, c; + + for(i = 0, c = 0; i < level->numholes; i++) + { + c += testhole(i, x, z); + } + + return c; +} + +// find the 1st hole here +int checkhole(float x, float z) +{ + int i; + + if(level == NULL) + { + return 0; + } + + for(i = 0; i < level->numholes; i++) + { + if(testhole(i, x, z)) + { + holez = i; + return 1; + } + } + return 0; +} + +// find all holes here within altitude1 and 2, return the count +int checkhole_between(float x, float z, float a1, float a2) +{ + int i, c; + + for(i = 0, c = 0; i < level->numholes; i++) + { + c += (testhole(i, x, z) && level->holes[i].height >= a1 && level->holes[i].height <= a2); + } + + return c; +} + +// get a highest hole below this altitude +int checkhole_in(float x, float z, float a) +{ + float maxa; + int i, ind; + + if(level == NULL) + { + return 0; + } + + maxa = -1; + ind = -1; + for(i = 0; i < level->numholes; i++) + { + if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a + { + maxa = level->holes[i].height; + ind = i; + } + } + + if ( ind >= 0 ) { + holez = ind; + return 1; + } else return 0; +} + +// find the hole id for highest hole +int checkholeindex_in(float x, float z, float a) +{ + float maxa; + int i, ind; + + if(level == NULL) + { + return -1; + } + + maxa = -1; + ind = -1; + for(i = 0; i < level->numholes; i++) + { + if(testhole(i, x, z) && level->holes[i].height+T_WALKOFF >= a && level->holes[i].height > maxa) // && level->holes[i].height+T_WALKOFF >= a + { + maxa = level->holes[i].height; + ind = i; + } + } + + return ind; +} + +// find the 1st hole id here +int checkhole_index(float x, float z) +{ + int i; + + if(level == NULL) + { + return -1; + } + + for(i = 0; i < level->numholes; i++) + { + if(testhole(i, x, z)) + { + //holez = i; + return i; + } + } + return -1; +} + +/* +Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from +the bottom of the platform and multiplying the result by the difference of the bottom left point and the top +left point divided by depth of the platform. The same is done for the right side, and checks to see if they are +within the bottom/top and the left/right area. +*/ +int testwall(int wall, float x, float z) +{ + float coef1, coef2; + + //if(wall >= level->numwalls || wall < 0) return 0; + if(z <= level->walls[wall].z && z >= level->walls[wall].z - level->walls[wall].depth) + { + coef1 = (level->walls[wall].z - z) * ((level->walls[wall].upperleft - level->walls[wall].lowerleft) / level->walls[wall].depth); + coef2 = (level->walls[wall].z - z) * ((level->walls[wall].upperright - level->walls[wall].lowerright) / level->walls[wall].depth); + if(x >= level->walls[wall].x + level->walls[wall].lowerleft + coef1 && x <= level->walls[wall].x + level->walls[wall].lowerright + coef2) + { + return 1; + } + } + + return 0; +} + +// find all walls here within altitude1 and 2, return the count +int checkwalls(float x, float z, float a1, float a2) +{ + int i, c; + + for(i = 0, c = 0; i < level->numwalls; i++) + { + c += (testwall(i, x, z) && level->walls[i].height >= a1 && level->walls[i].height <= a2); + } + + return c; +} + +// get a highest wall below this altitude +int checkwall_below(float x, float z, float a) +{ + float maxa; + int i, ind; + + if(level == NULL) + { + return -1; + } + + maxa = 0; + ind = -1; + for(i = 0; i < level->numwalls; i++) + { + if(testwall(i, x, z) && level->walls[i].height <= a && level->walls[i].height > maxa) + { + maxa = level->walls[i].height; + ind = i; + } + } + + return ind; +} + +// return the 1st wall found here +int checkwall_index(float x, float z) +{ + int i; + if(level == NULL) + { + return WALL_INDEX_NONE; + } + + for (i = 0; i < level->numwalls; i++) + { + if (testwall(i, x, z)) + { + return i; + } + } + + return WALL_INDEX_NONE; +} + +/* +Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from +the bottom of the platform and multiplying the result by the difference of the bottom left point and the top +left point divided by depth of the platform. The same is done for the right side, and checks to see if they are +within the bottom/top and the left/right area. +*/ +int testplatform(entity *plat, float x, float z, entity *exclude) +{ + float coef1, coef2; + float offz, offx; + if(plat == exclude) + { + return 0; + } + if(!plat->animation || !plat->animation->platform || !plat->animation->platform[plat->animpos][PLATFORM_HEIGHT]) + { + return 0; + } + offz = plat->position.z + plat->animation->platform[plat->animpos][PLATFORM_Z]; + offx = plat->position.x + plat->animation->platform[plat->animpos][PLATFORM_X]; + if(z <= offz && z >= offz - plat->animation->platform[plat->animpos][PLATFORM_DEPTH]) + { + coef1 = (offz - z) * ((plat->animation->platform[plat->animpos][PLATFORM_UPPERLEFT] - + plat->animation->platform[plat->animpos][PLATFORM_LOWERLEFT]) / plat->animation->platform[plat->animpos][PLATFORM_DEPTH]); + coef2 = (offz - z) * ((plat->animation->platform[plat->animpos][PLATFORM_UPPERRIGHT] - + plat->animation->platform[plat->animpos][PLATFORM_LOWERRIGHT]) / plat->animation->platform[plat->animpos][PLATFORM_DEPTH]); + + if(x >= offx + plat->animation->platform[plat->animpos][PLATFORM_LOWERLEFT] + coef1 && + x <= offx + plat->animation->platform[plat->animpos][PLATFORM_LOWERRIGHT] + coef2) + { + return 1; + } + } + return 0; +} + +//find the first platform between these 2 altitudes +entity *check_platform_between(float x, float z, float amin, float amax, entity *exclude) +{ + entity *plat = NULL; + int i; + + if(level == NULL) + { + return NULL; + } + + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) ) + { + plat = ent_list[i]; + if(plat->position.y <= amax && plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > amin) + { + return plat; + } + } + } + return NULL; +} + +//find a lowest platform above this altitude +entity *check_platform_above(float x, float z, float a, entity *exclude) +{ + float mina; + entity *plat = NULL; + int i, ind; + + if(level == NULL) + { + return NULL; + } + + mina = 9999999; + ind = -1; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) ) + { + plat = ent_list[i]; + if(plat->position.y >= a && plat->position.y < mina) + { + mina = plat->position.y; + ind = i; + } + } + } + return (ind >= 0) ? ent_list[ind] : NULL; +} + +//find a highest platform below this altitude +entity *check_platform_below(float x, float z, float a, entity *exclude) +{ + float maxa; + entity *plat = NULL; + int i, ind; + + if(level == NULL) + { + return NULL; + } + + maxa = MIN_INT; + ind = -1; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude) ) + { + plat = ent_list[i]; + if(plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] <= a && + plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > maxa) + { + maxa = plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT]; + ind = i; + } + } + } + return (ind >= 0) ? ent_list[ind] : NULL; +} + +entity *check_platform_above_entity(entity *e) +{ + float mina; + int heightvar; + entity *plat = NULL; + int i, ind; + + if(level == NULL) + { + return NULL; + } + if(e->animation->size.y) + { + heightvar = e->animation->size.y; + } + else + { + heightvar = e->modeldata.size.y; + } + + mina = T_MAX_CHECK_ALTITUDE; + ind = -1; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) ) + { + plat = ent_list[i]; + if(plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT] > e->position.y + heightvar && plat->position.y < mina) + { + mina = plat->position.y; + ind = i; + } + } + } + return (ind >= 0) ? ent_list[ind] : NULL; +} + +entity *check_platform_below_entity(entity *e) +{ + float maxa, a; + entity *plat = NULL; + int i, ind; + + if(level == NULL) + { + return NULL; + } + + maxa = MIN_INT; + ind = -1; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], e->position.x, e->position.z, e) ) + { + plat = ent_list[i]; + a = plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT]; + if(a < e->position.y + T_WALKOFF && plat->position.y < e->position.y && a > maxa) + { + maxa = a; + ind = i; + } + } + } + return (ind >= 0) ? ent_list[ind] : NULL; +} + +float checkbase(float x, float z, float y, entity *ent) +{ + float maxbase = -1.0f, base = -1.0f; + int index = -1; + entity *platform = NULL; + + base = (checkhole(self->position.x, self->position.z)) ? -1.0f : 0.0f; + if (base > maxbase) maxbase = base; + + base = check_basemap(self->position.x, self->position.z); + if (base > maxbase) maxbase = base; + + base = -1.0f; + if((index = checkwall_below(x, z, T_MAX_CHECK_ALTITUDE)) >= 0) + { + base = level->walls[index].height; + if (base > maxbase) maxbase = base; + } + + base = -1.0f; + platform = check_platform_below(x, z, y, ent); + if (platform != NULL) + { + base = get_platform_base(platform); + if (base > maxbase) maxbase = base; + } + + return maxbase; +} + +// find the 1st platform entity here +entity *check_platform(float x, float z, entity *exclude) +{ + int i; + if(level == NULL) + { + return NULL; + } + + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && testplatform(ent_list[i], x, z, exclude)) + { + return ent_list[i]; + } + } + return NULL; +} + +float get_platform_base(entity *plat) +{ + float alt = 0; + + alt = plat->position.y; + alt += plat->animation->platform[plat->animpos][PLATFORM_HEIGHT]; + + return alt; +} + +int is_on_platform(entity *ent) +{ + entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent); + if (plat) + { + if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return 1; + } + + return 0; +} + +entity *get_platform_on(entity *ent) +{ + entity *plat = check_platform_below(ent->position.x,ent->position.z,ent->position.y+T_WALKOFF,ent); + if (plat) + { + if ( diff(ent->base,get_platform_base(plat)) <= T_WALKOFF ) return plat; + } + + return NULL; +} + +// for adjust grab position function, test if an entity can move from a to b +// TODO: check points between the two pionts, if necessary +// special return values: +// -1: ent can jump over walls, for jump check +// -2: for hole ai reaction check +int testmove(entity *ent, float sx, float sz, float x, float z) +{ + entity *other = NULL, *platbelow = NULL; + int wall, heightvar; + float xdir, zdir; + + xdir = x - sx; + zdir = z - sz; + + if(!xdir && !zdir) + { + return 1; + } + + // -----------bounds checking--------------- + // Subjec to Z and out of bounds? Return to level! + if (ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MIN_Z) + { + if(zdir && z < PLAYER_MIN_Z) + { + return 0; + } + } + + if (ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MAX_Z) + { + if(zdir && z > PLAYER_MAX_Z) + { + return 0; + } + } + + // screen checking + // Kratus (29-04-21) Reduced the "screen checking" range from 10 to 5 to avoid the entities to stuck in the edge of the screen + // This change was made because the "common_trymove" function also has another "screen checking" with a range of 10 too + // If the "testmove" function has a equal or bigger range than the "common_trymove" function, sometimes the entities will stuck + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_SCREEN) + { + if(x < advancex + 5) + { + return 0; + } + else if(x > advancex + (videomodes.hRes - 5)) + { + return 0; + } + } + //-----------end of bounds checking----------- + + //-------------hole checking --------------------- + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_HOLE) + { + if(checkhole(x, z) && checkwall_index(x, z) < 0 && !check_platform_below(x, z, ent->position.y, ent)) + { + return -2; + } + } + //-----------end of hole checking--------------- + + //--------------obstacle checking ------------------ + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_OBSTACLE) + { + if((other = find_ent_here(ent, x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) && + (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT])) + { + return 0; + } + } + //-----------end of obstacle checking-------------- + + // ---------------- platform checking---------------- + + if(ent->animation->size.y) + { + heightvar = ent->animation->size.y; + } + else + { + heightvar = ent->modeldata.size.y; + } + + // Check for obstacles with platform code and adjust base accordingly + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM && (other = check_platform_between(x, z, ent->position.y, ent->position.y + heightvar, ent)) ) + { + platbelow = check_platform_below(x, z, ent->position.y+T_WALKOFF, ent); + if ( !platbelow ) return 0; + else + { + float palt = get_platform_base(platbelow); + if ( other != platbelow && diff(ent->position.y,palt) > T_WALKOFF ) return 0; + } + } + //-----------end of platform checking------------------ + + // ------------------ wall checking --------------------- + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_WALL && (wall = checkwall_below(x, z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > ent->position.y) + { + if(validanim(ent, ANI_JUMP) && sz < level->walls[wall].z && sz > level->walls[wall].z - level->walls[wall].depth) //Can jump? + { + //rmin = (float)ent->modeldata.animation[ANI_JUMP]->range.x.min; + //rmax = (float)ent->modeldata.animation[ANI_JUMP]->range.x.max; + if(level->walls[wall].height < ent->position.y + ent->modeldata.animation[ANI_JUMP]->range.x.max) + { + return -1; + } + } + return 0; + } + //----------------end of wall checking-------------- + + return 1; + +} + +// find real opponent +void set_opponent(entity *ent, entity *other) +{ + entity *realself, *realother; + + if(!ent) + { + return; + } + + realself = ent; + while(realself->owner) + { + realself = realself->owner; + } + + realother = other; + while(realother && realother->owner) + { + realother = realother->owner; + } + + realself->opponent = ent->opponent = realother; + if(realother) + { + realother->opponent = other->opponent = realself; + } + +} + +/* +* Caskey, Damon V. +* 2023-04-10 +* +* Accept string input and return +* matching constant. +*/ +e_pain_config_flags pain_get_config_flag_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_pain_config_flags flag; + } item_lookup_table[] = { + {"none", PAIN_CONFIG_NONE}, + {"back_pain", PAIN_CONFIG_BACK_PAIN}, + {"fall_disable", PAIN_CONFIG_FALL_DISABLE}, + {"fall_disable_air", PAIN_CONFIG_FALL_DISABLE_AIR}, + {"pain_disable", PAIN_CONFIG_PAIN_DISABLE} + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown pain config flag (%s). \n", value); + return PAIN_CONFIG_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read pain configuration arguments directly from the source + line and combine their corresponding behavior flags. +*/ +e_pain_config_flags pain_get_config_flags_from_command_line( + const char* command_line +) +{ + const char* value; + s_command_argument_reader reader; + e_pain_config_flags result = PAIN_CONFIG_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= pain_get_config_flag_from_string(value); + } + + return result; +} + + +/* +* Caskey, Damon V. +* 2023-04-05 +* +* Accept string input and return +* matching constant. +*/ +e_block_config_flags block_get_config_flag_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_block_config_flags flag; + } item_lookup_table[] = { + {"none", BLOCK_CONFIG_NONE}, + {"active", BLOCK_CONFIG_ACTIVE}, + {"back", BLOCK_CONFIG_BACK}, + {"disabled", BLOCK_CONFIG_DISABLED}, + {"hold_impact", BLOCK_CONFIG_HOLD_IMPACT}, + {"hold_infinite", BLOCK_CONFIG_HOLD_INFINITE} + }; + + const size_t list_count = sizeof(item_lookup_table) / sizeof(*item_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, item_lookup_table[i].text_name) == 0) + { + return item_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * unknown flag. + */ + + printf("\n\n Unknown block config flag (%s). \n", value); + return BLOCK_CONFIG_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read blocking configuration arguments directly from the + source line and combine their corresponding behavior flags. +*/ +e_block_config_flags block_get_config_flags_from_command_line( + const char* command_line +) +{ + const char* value; + s_command_argument_reader reader; + e_block_config_flags result = BLOCK_CONFIG_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= block_get_config_flag_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2018-12-31 +* +* Initialize appropriate block animation and flags. Called when +* entity blocks actively (blocking before attack hits). Used +* by all player controlled entities or AI controlled entities +* with active block enabled. +*/ +void do_active_block(entity *ent) +{ + /* Run blocking action. */ + ent->takeaction = common_block; + + /* Stop movement. */ + ent->velocity.x = 0; + ent->velocity.z = 0; + + /* Set flags. */ + set_blocking(ent); + + /* End combo. */ + ent->combostep[0] = 0; + + /* + * If we have a block tranisiton animation, use it. Otherwise + * go right to block. + */ + if (validanim(ent, ANI_BLOCKSTART)) + { + ent_set_anim(ent, ANI_BLOCKSTART, 0); + } + else + { + ent_set_anim(ent, ANI_BLOCK, 0); + } +} + +/* +* Caskey, Damon V. +* 2018-09-16 +* +* Find out if attack can be blocked by entity. +* This function is concerned with the attack +* vs. entity in terms of game mechanics like +* guard break, attack type vs. defense, and +* so on. It does not handle rules for AI blocking. +*/ +bool check_blocking_eligible(entity *ent, entity *other, s_attack *attack, s_body *body, e_block_state_flags block_state) { + int temp_block_threshold = 0; + + /* Blocking must be active regardless of behavior overrides. */ + if (!(block_state & BLOCK_STATE_ACTIVE)) { + return false; + } + + /* Guard points only limit native blocking unless explicitly ignored. */ + if ((block_state & BLOCK_STATE_NATIVE) + && !(block_state & BLOCK_STATE_IGNORE_GUARD_POINTS) + && ent->modeldata.guardpoints > 0 + && ent->guardpoints <= 0) { + return false; + } + + /* Attack from behind? Can't block without an applicable override. */ + if (!(block_state & BLOCK_STATE_IGNORE_DIRECTION) + && ent->direction == other->direction + && !(ent->modeldata.block_config_flags & BLOCK_CONFIG_BACK)) { + return false; + } + + /* + * Attack eligibility consists of penetration vs. power and the + * combined model plus defense threshold. Direction and guard + * points have independent override flags above. + */ + if (!(block_state & BLOCK_STATE_IGNORE_ATTACK_ELIGIBILITY)) { + const s_defense* defense_object = defense_find_current_object(ent, body, attack->attack_type); + + if ((attack->no_block || defense_object->blockpower) + && attack->no_block >= defense_object->blockpower) { + return false; + } + + temp_block_threshold = ent->modeldata.thold + defense_object->blockthreshold; + + if (temp_block_threshold > attack->attack_force) { + return false; + } + } + + return true; +} + +// Caskey, Damon V. +// 2018-09-19 +// +// Mandatory conditions the AI must pass before it +// can decide to block. These are not rules for +// blocking in general. +bool check_blocking_rules(entity *ent) { + // If already blocking we can + // forget the rest and return + // true right away. + if (ent->blocking & BLOCK_STATE_ACTIVE) { + return true; + } + + // No blocking animation? + if (!validanim(ent, ANI_BLOCK)) { + return false; + } + + // Have to be idle. + if (!ent->idling) { + return false; + } + + // AI can't be attacking. + if (ent->attacking == ATTACKING_ACTIVE) { + return false; + } + + // Grappling? + if (ent->link) { + return false; + } + + // Airborne? + if (inair(ent)) { + return false; + } + + // Frozen? + if (ent->frozen) { + return false; + } + + // Falling? + if (ent->falling) { + return false; + } + + return true; +} + +// Caskey, Damon V. +// 2018-09-17 +// +// AI blocking decision. Handles AI's chances +// to block. Returns true if AI chooses to attempt +// a block. +bool check_blocking_decision(entity *ent) { + /* Per-instance override supplied through the blocking state. */ + if (ent->blocking & BLOCK_STATE_IGNORE_CHANCE) { + return true; + } + + // If we have active block enabled and we're + // already blocking, then we want the AI to + // keep blocking (like most players would). + if (ent->modeldata.block_config_flags & BLOCK_CONFIG_ACTIVE) { + if (ent->blocking & BLOCK_STATE_ACTIVE) { + return true; + } + } + + // Kratus (10-2021) Fixed the random blocking chance according with the "blockodds" value defined by the modder + // Now it works as intended (1 = block all / 2147483647 = never block) + // Run random chance against blockodds. If it + // passes, AI will block. + if ((rand32()&ent->modeldata.blockodds) == 0) { + return true; + } + + // If we got this far, we never decided to + // block, so return false. + return false; +} + +// Caskey, Damon V. +// 2018-09-17 +// +// Runs all blocking conditions and returns the active +// blocking state, or BLOCK_STATE_NONE if the attack +// should not be blocked. +e_block_state_flags check_blocking_master(entity *ent, entity *other, s_attack *attack, s_body *body) { + e_entity_type entity_type; + e_block_state_flags block_state; + + entity_type = ent->modeldata.type; + block_state = ent->blocking; + + if (ent->modeldata.block_config_flags & BLOCK_CONFIG_DISABLED) { + return BLOCK_STATE_NONE; + } + + /* + * Script-controlled blocking is already an explicit choice. It + * bypasses native initiation rules and chance while retaining + * common attack eligibility unless its individual bits disable it. + */ + if ((block_state & BLOCK_STATE_ACTIVE) + && !(block_state & BLOCK_STATE_NATIVE)) { + if (!check_blocking_eligible(ent, other, attack, body, block_state)) { + return BLOCK_STATE_NONE; + } + + return block_state; + } + + /* Players must have entered native blocking before impact. */ + if (entity_type & TYPE_PLAYER) { + if (!(block_state & BLOCK_STATE_ACTIVE)) { + return BLOCK_STATE_NONE; + } + } else { + /* AI either continues native blocking or chooses it on impact. */ + if (!check_blocking_rules(ent) || !check_blocking_decision(ent)) { + return BLOCK_STATE_NONE; + } + + block_state |= BLOCK_STATE_ACTIVE | BLOCK_STATE_NATIVE; + } + + if (!check_blocking_eligible(ent, other, attack, body, block_state)) { + return BLOCK_STATE_NONE; + } + + return block_state; +} + +/* +* Caskey, Damon V. +* 2018-09-18 +* +* Apply primary block settings, animations, +* actions, and scripts. +*/ +void set_blocking_action(entity *ent, entity *other, s_attack *attack, e_block_state_flags block_state) +{ + /* Execute the attacker's didhit script with blocked flag. */ + execute_didhit_script(other, ent, attack, 1); + + /* Native blocking owns the action, movement, and guard system. */ + if (block_state & BLOCK_STATE_NATIVE) { + ent->blocking |= block_state; + set_blocking(ent); + block_state = ent->blocking; + ent->takeaction = common_block; + + ent->velocity.x = 0; + ent->velocity.z = 0; + + if (!(block_state & BLOCK_STATE_IGNORE_GUARD_POINTS) + && ent->modeldata.guardpoints > 0) { + ent->guardpoints -= attack->guardcost; + } + } + + /* + * Blocked hit is still a hit, so + * increment the attacker's hit counter. + */ + ++other->animation->hit_count; + + /* Execute our block script. */ + execute_didblock_script(ent, other, attack); +} + +/* +* Caskey, Damon V. +* 2018-09-18 +* +* Return true if attack force is sufficient +* to cause blocking pain. Otherwise return false. +*/ +bool check_blocking_pain(const entity *ent, const s_attack *attack) { + + if (attack->attack_force >= ent->modeldata.blockpain) { + return true; + } + + return false; +} + +// Caskey, Damon V. +// 2018-09-21 +// +// Place entity into appropriate blocking animation. +void set_blocking_animation(entity *acting_entity, s_attack *attack, e_block_state_flags block_state) { + + /* + * Entity must have a valid blockpain and + * attack must meet or exceed the blockpain + * threshold. Otherwise, we just go to the + * normal block animation. + */ + + if(!(block_state & BLOCK_STATE_IGNORE_BLOCKPAIN) + && check_blocking_pain(acting_entity, attack) + && set_blockpain(acting_entity, attack->attack_type, 1)) { + return; + } + + ent_set_anim(acting_entity, ANI_BLOCK, 0); +} + +// Caskey, Damon V. +// 2018-09-21 +// +// Perform a block. +void do_passive_block(entity *ent, entity *other, s_attack *attack, e_block_state_flags block_state) +{ + /* Script-controlled blocks preserve the creator-controlled action. */ + if (block_state & BLOCK_STATE_NATIVE) { + set_blocking_animation(ent, attack, block_state); + } + + // Spawn the blocking flash. + spawn_attack_flash(ent, attack, attack->flash.model_block, ent->modeldata.flash.model_block); + + // Run blocking actions and scripts. + set_blocking_action(ent, other, attack, block_state); +} + +/* +* Caskey, Damon V. +* 2018-09-18 +* +* Handle flash spawning for hits. If conditions +* are met, spawns and prepares an appropriate +* flash effect entity at hit location. +* +* Returns pointer to flash entity if spawned, +* or NULL if not. +*/ +entity *spawn_attack_flash(entity *ent, s_attack *attack, int attack_flash, int model_flash) +{ + //printf("\n\n spawn_attack_flash(%p, %p, %d, %d)", ent, attack, attack_flash, model_flash); + + int model_index = MODEL_INDEX_NONE; // THe model we will spawn as flash entity. + entity *flash = NULL; // Flash entity pointer. + float attacker_position_z = lasthit.position.z; + float target_position_z = lasthit.position.z; + int set_layer = 0; + float position_z = 0.0; + int flash_layer_source = 0; + int flash_z_source = 0; + + /* + * Flash disabled by attack? + * We're done. Do nothing and exit. + */ + + if (attack->no_flash) + { + return NULL; + } + + /* + * If the model doesn't allow incoming custom + * flash effects, default to the model's global + * flash. + * + * Otherwise we need to see if the custom + * attack flash index is valid. If it is, then + * we will use it to spawn a flash effect. + */ + + if (!ent->modeldata.noatflash) + { + /* Valid custom flash index ? */ + if (attack_flash >= 0) + { + model_index = attack_flash; + } + else + { + model_index = model_flash; + } + } + else + { + model_index = model_flash; + } + + /* + * Get the attacker and target Z position. + * We are using local variables here just + * in case the attacker or target are not + * populated with valid entities. + */ + + if (lasthit.attacker) + { + attacker_position_z = lasthit.attacker->position.z; + } + + if (ent) + { + target_position_z = ent->position.z; + } + + /* + * Base Z position. This is the starting Z location + * to spawn flash. Start with global config value + * then apply adjustments (if any) from collison + * objects. Then we determine source for base Z + * using the total. + */ + + flash_z_source = global_config.flash.z_source; + flash_z_source += lasthit.attack ? lasthit.attack->flash.z_source : 0; + flash_z_source += lasthit.detect_body ? lasthit.detect_body->flash.z_source : 0; + + if (!flash_z_source) + { + position_z = lasthit.position.z; + } + else if (flash_z_source <= -255) + { + position_z = attacker_position_z < target_position_z ? attacker_position_z : target_position_z; + } + else if (flash_z_source > -255 && flash_z_source < 0) + { + position_z = attacker_position_z; + } + else if (flash_z_source > 0 && flash_z_source < 255) + { + position_z = target_position_z; + } + else // >= 255 + { + position_z = attacker_position_z > target_position_z ? attacker_position_z : target_position_z; + } + + flash = spawn(lasthit.position.x, position_z, lasthit.position.y, DIRECTION_LEFT, NULL, model_index, NULL); + + if (!flash) + { + return NULL; + } + + /* + * Adjust the layer (sort id). Combine values + * from attack, defense and global adjustment. + * + * Then we decide what to do from the total: + * + * 0: Use highest Z (attacker vs. recipient). + * <0: Use attacker. + * >0: Use defender. + * + * Then we combine all static adjustments and + * apply to flash model's sort ID. + */ + + //printf("\n\t global_config.flash_layer_source: %d", global_config.flash_layer_source); + + flash_layer_source = global_config.flash.layer_source; + flash_layer_source += lasthit.attack ? lasthit.attack->flash.layer_source : 0; + flash_layer_source += lasthit.detect_body ? lasthit.detect_body->flash.layer_source : 0; + + //printf("\n\t flash_layer_source: %d", flash_layer_source); + + if (!flash_layer_source) + { + set_layer = position_z; + } + else if (flash_layer_source <= -255) + { + set_layer = attacker_position_z < target_position_z ? attacker_position_z : target_position_z; + } + else if (flash_layer_source > -255 && flash_layer_source < 0) + { + set_layer = attacker_position_z; + } + else if (flash_layer_source > 0 && flash_layer_source < 255) + { + set_layer = target_position_z; + } + else // >= 255 + { + set_layer = attacker_position_z > target_position_z ? attacker_position_z : target_position_z; + } + + //printf("\n\t set_layer: %d", set_layer); + + set_layer += global_config.flash.layer_adjust + lasthit.attack->flash.layer_adjust + lasthit.detect_body->flash.layer_adjust; + + //printf("\n\t set_layer (adjusted): %d", set_layer); + + flash->modeldata.setlayer = set_layer; + + //printf("\n\t flash->modeldata.setlayer: %d", flash->modeldata.setlayer); + + /* + * We have a valid flash entity, let's + * set up its basic properties. + */ + + flash->spawntype = SPAWN_TYPE_FLASH; + flash->base = lasthit.position.y; + flash->autokill |= AUTOKILL_ANIMATION_COMPLETE; + + /* + * If flipping enabled, flip the flash right + * if hit is to right of target entity's + * position. + */ + + if (flash->modeldata.toflip) + { + if (lasthit.position.x > ent->position.x) + { + flash->direction = DIRECTION_RIGHT; + } + } + else + { + /* + * Kratus (10-2021) If the flag is 0, the flash will get the same direction as the defender + * This change was made to avoid the "random" direction applied by "toflip 1", because it depends on the "lasthit" position + * Without this line, the flash will never change the direction according to the defender's facing when "toflip" is 0 + */ + + flash->direction = (ent->direction); + } + + /* + * Run flash's spawn script and + * return the flash pointer. + */ + + execute_onspawn_script(flash); + + return flash; +} + +/* +* Caskey, Damon V. +* 2019-11-24 +* +* Check follow up conditions. Return true +* if all conditions pass, false otherwise. +*/ +int check_follow_up_condition(entity *ent, entity *target, s_anim *animation, int blocked) +{ + if (!animation->followup.animation) + { + return 0; + } + + /* No follow up allowed. */ + + if (animation->followup.condition & FOLLOW_CONDITION_NONE) + { + return 0; + } + + /* Always do follow up. */ + + if (animation->followup.condition & FOLLOW_CONDITION_ANY) + { + return 1; + } + + /* Block attack. */ + + if (animation->followup.condition & FOLLOW_CONDITION_BLOCK_FALSE) + { + if (blocked) + { + return 0; + } + } + + if (animation->followup.condition & FOLLOW_CONDITION_BLOCK_TRUE) + { + if (!blocked) + { + return 0; + } + } + + /* Possible to grab target. */ + + if (animation->followup.condition & FOLLOW_CONDITION_GRAB_FALSE) + { + if (check_cangrab(ent, target)) + { + return 0; + } + } + + if (animation->followup.condition & FOLLOW_CONDITION_GRAB_TRUE) + { + if (!check_cangrab(ent, target)) + { + return 0; + } + } + + /* We are hostile toward target. */ + + if (animation->followup.condition & FOLLOW_CONDITION_HOSTILE_ATTACKER_FALSE) + { + if (faction_check_is_hostile(ent, target)) + { + return 0; + } + } + + if (animation->followup.condition & FOLLOW_CONDITION_HOSTILE_ATTACKER_TRUE) + { + if (!faction_check_is_hostile(ent, target)) + { + return 0; + } + } + + /* Target is hostile toward us. */ + + if (animation->followup.condition & FOLLOW_CONDITION_HOSTILE_TARGET_FALSE) + { + if (faction_check_is_hostile(target, ent)) + { + return 0; + } + } + + if (animation->followup.condition & FOLLOW_CONDITION_HOSTILE_TARGET_TRUE) + { + if (!faction_check_is_hostile(target, ent)) + { + return 0; + } + } + + /* Lethal damage. */ + + if (animation->followup.condition & FOLLOW_CONDITION_LETHAL_FALSE) + { + if (target->energy_state.health_current <= 0) + { + return 0; + } + } + + if (animation->followup.condition & FOLLOW_CONDITION_LETHAL_TRUE) + { + if (target->energy_state.health_current > 0) + { + return 0; + } + } + + /* If all checks passed, return true. */ + + return 1; +} + +/* +* Caskey, Damon V. +* 2019-11-24 +* +* Attempt to perform follow up animation. +* If successful, sets entity animation to +* appropriate follow up and returns true. +*/ +int try_follow_up(entity *ent, entity *target, s_anim *animation, int didblock) +{ + animation_id_t animation_id = ANI_NONE; + + /* If we don't have a follow action, get out. */ + + if (!animation->followup.animation) + { + return 0; + } + + /* Must meet follow up conditions. */ + + if (!check_follow_up_condition(ent, target, animation, didblock)) + { + return 0; + } + + /* If we have the animation, then execute it now. */ + + animation_id = animfollows[animation->followup.animation - 1]; + + if (validanim(ent, animation_id)) + { + ent_set_anim(ent, animation_id, 1); + + return 1; + } + + return 0; +} + +/* +* Caskey, Damon V. +* 2019-12-03 +* +* Verify an attack meets conditions to trigger a counter action. +*/ +bool check_counter_condition(entity* target, entity* attacker, s_attack* attack_object, s_body* body_object) { + + s_counter_action* counter = &target->animation->counter_action; + int64_t force = 0; + + /* If there's no condition, get out now. */ + if (!counter->condition) { + return false; + } + + /* Verify in the frame range. */ + if (target->animpos < counter->frame.min || target->animpos > counter->frame.max) { + return false; + } + + /* Now we verify condition flags. */ + + /* Always is always... */ + if (counter->condition == COUNTER_ACTION_CONDITION_ANY) { + return true; + } + + /* In the back ? */ + if (counter->condition & COUNTER_ACTION_CONDITION_BACK_FALSE) { + if (target->direction == attacker->direction) { + return false; + } + } + + if (counter->condition & COUNTER_ACTION_CONDITION_BACK_TRUE) { + if (target->direction != attacker->direction) { + return false; + } + } + + /* We need defense object for subsequent checks. */ + const s_defense* defense_object = defense_find_current_object(target, body_object, attack_object->attack_type); + + /* Blockable ? */ + if (counter->condition & COUNTER_ACTION_CONDITION_BLOCK_FALSE) { + if (attack_object->no_block <= defense_object->blockpower) { + return false; + } + } + + if (counter->condition & COUNTER_ACTION_CONDITION_BLOCK_TRUE) { + if (attack_object->no_block > defense_object->blockpower) { + return false; + } + } + + /* Vs.lethal / non - lethal damage. */ + force = calculate_force_damage(target, attacker, attack_object, defense_object, FALSE); + + if (counter->condition & COUNTER_ACTION_CONDITION_DAMAGE_LETHAL_FALSE) { + if (target->energy_state.health_current <= force) { + return false; + } + } + + if (counter->condition & COUNTER_ACTION_CONDITION_DAMAGE_LETHAL_TRUE) { + if (target->energy_state.health_current > force) { + return false; + } + } + + /* Freeze attack ? */ + if (counter->condition & COUNTER_ACTION_CONDITION_FREEZE_FALSE) { + if (attack_object->freeze) { + return false; + } + } + + if (counter->condition & COUNTER_ACTION_CONDITION_FREEZE_TRUE) { + if (!attack_object->freeze) { + return false; + } + } + + /* Attacker hostile to us ? */ + if (counter->condition == COUNTER_ACTION_CONDITION_HOSTILE_ATTACKER_FALSE) { + if (faction_check_is_hostile(attacker, target)) { + return false; + } + } + + if (counter->condition == COUNTER_ACTION_CONDITION_HOSTILE_ATTACKER_TRUE) { + if (!faction_check_is_hostile(attacker, target)) { + return false; + } + } + + /* Hostile to attacker ? */ + if (counter->condition == COUNTER_ACTION_CONDITION_HOSTILE_TARGET_FALSE) { + if (faction_check_is_hostile(target, attacker)) { + return false; + } + } + + if (counter->condition == COUNTER_ACTION_CONDITION_HOSTILE_TARGET_TRUE) { + if (!faction_check_is_hostile(target, attacker)) { + return false; + } + } + + /* Passed all checks. We can return true. */ + return true; +} + +/* +* Caskey, Damon V. +* 2019-12-04 +* +* Attempt to perform counter action animation. +* If successful, sets entity animation to +* appropriate counter and returns true. +*/ +bool try_counter_action(entity* target, entity* attacker, s_attack* attack_object, s_body* body_object) { + + /* + * If we don't have a follow animation to use + * for counter, get out. + */ + + if (!target->animation->followup.animation) { + return false; + } + + /* Must meet counter action conditions. */ + + if (!check_counter_condition(target, attacker, attack_object, body_object)) { + return false; + } + + /* Take damage from attack ? */ + + if (target->animation->counter_action.damaged == COUNTER_ACTION_TAKE_DAMAGE_NORMAL) { + + /* We need the real damage. */ + const s_defense* defense_object = defense_find_current_object(target, body_object, attack_object->attack_type); + + const int64_t force = calculate_force_damage(target, attacker, attack_object, defense_object, FALSE); + + /* Revert lethal damage to 1. */ + if (target->energy_state.health_current - force <= 0) { + target->energy_state.health_current = 1; + + } else { + target->energy_state.health_current -= force; + } + } + + /* Set counter animation if we can. */ + + const animation_id_t current_follow_id = animfollows[target->animation->followup.animation - 1]; + + if (validanim(target, current_follow_id)) { + if (!target->modeldata.animation[current_follow_id]->attack_one) { + target->modeldata.animation[current_follow_id]->attack_one = target->animation->attack_one; + } + ent_set_anim(target, current_follow_id, 0); + } + + /* Flash spawn. */ + spawn_attack_flash(target, attack_object, attack_object->flash.model_block, target->modeldata.flash.model_block); + + return true; +} + +/* +* Caskey, Damon V. +* 2021-09-04 +* +* Update attack IDs to avoid single attack +* hitting on every update. Original concept +* of multiple attack IDs by Kratus. Migrated +* to array and encapsulated into function +* by DC. +*/ +void attack_update_id(entity* acting_entity, int attack_id) +{ + int i = 0; + int i_source = 0; + + /* + * Loop backward from highest element to second lowest. + * At each iteration, update the current attack ID + * element in array with value from element one lower + * in order. + * + * Ex: array[4] = array[3] + */ + for(i = MAX_ATTACK_IDS-1; i > 0; i--) + { + i_source = i - 1; + + acting_entity->attack_id_incoming[i] = acting_entity->attack_id_incoming[i_source]; + } + + /* Update element 0 with supplied ID. */ + acting_entity->attack_id_incoming[0] = attack_id; +} + +/* +* Caskey, Damon V. +* 2021-09-04 +* +* Compare supplied attack ID with existing +* attack IDs. Returns true if any match or +* if rule exceptions from attack or multihit +* are enabled. +*/ +int attack_id_check_match(entity* acting_entity, s_attack* attack_object, int attack_id, int multihit) +{ + int i = 0; + int max_id = MAX_ATTACK_IDS; + + /* + * Attack is allowed to ignore ID checks, so + * just return true now. + */ + if (attack_object->ignore_attack_id) + { + return 1; + } + + /* + * If mutihit is enabled, we only want + * check the first ID. Set our max to 1 + * so loop only runs for element 0. + */ + if (multihit) + { + max_id = 1; + } + + /* + * If any array element value matches supplied + * attack ID, we return true. + */ + for (i = 0; i < max_id; i++) + { + if (acting_entity->attack_id_incoming[i] == attack_id) + { + return 1; + } + } + + /* Couldn't find any match or exception. Return false. */ + return 0; +} + +/* +* Caskey, Damon V. +* 2026-07-23 +* +* Invoke legacy item collection with the +* collecting entity as self. +* +* didfind_item() does not yet accept the +* collector explicitly. Isolate that compatibility +*requirement here instead of exposing do_attack() to self. +*/ +static void do_attack_invoke_didfind_item(entity* target_entity, entity* item_entity) { + entity* previous_self = self; + + self = target_entity; + didfind_item(item_entity); + self = previous_self; +} + +void do_attack(entity *attacking_entity) { + int i = 0; + int64_t force = 0; + e_blocktype blocktype = BLOCK_TYPE_MP_FIRST; + entity* def = NULL; + entity* topowner = NULL; + entity* otherowner = NULL; + entity* target = NULL; + s_anim* current_anim = NULL; + s_attack* attack = NULL; + s_body* target_body_object = NULL; + const s_defense* defense_object = NULL; + bool didhit = false; // So a different sound effect can be played when an attack hits + bool didblock = false; // So a different sound effect can be played when an attack is blocked + e_block_state_flags block_state = BLOCK_STATE_NONE; + uint64_t current_attack_id = 0; + //int hit_detected = 0; // Has a hit been detected? + + +#define followed (current_anim!=attacking_entity->animation) + static uint64_t new_attack_id = 1; + + // Can't get hit after this + if(level_completed) { + return; + } + + topowner = attacking_entity; // trace the top owner, for projectile combo checking :) + while(topowner->owner) { + topowner = topowner->owner; + } + + // If any blast active, use indirect damage downstream. + const bool indirect = attacking_entity->projectile != BLAST_NONE ? true : false; + + // Every attack gets a unique ID to make sure no one + // gets hit more than once by the same attack + current_attack_id = attacking_entity->attack_id_outgoing; + + if(!current_attack_id) { + ++new_attack_id; + if(new_attack_id == 0) { + new_attack_id = 1; + } + attacking_entity->attack_id_outgoing = current_attack_id = new_attack_id; + } + + + current_anim = attacking_entity->animation; + + for(i = 0; i < ent_max && !followed; i++) { + target = ent_list[i]; + + if(!target->exists) { + continue; + } + + // Check collision. If a collision + // is found, the impacting + // collision pointers are also + // populated into lasthit, which + // we will use below. + if(!checkhit(attacking_entity, target)) { + continue; + } + + attack = lasthit.attack; + force = attack->attack_force; + target_body_object = lasthit.detect_collision_body->body; + defense_object = defense_find_current_object(target, target_body_object, attack->attack_type); + + // Verify target is alive. + if(target->death_state & DEATH_STATE_DEAD) { + continue; + } + + // Verify target is invincible, + // or attack type is an item. + // This is to allow item collection + // even while invincible. + if(target->invincible & INVINCIBLE_INTANGIBLE) { + if(attack->attack_type != ATK_ITEM) { + continue; + } + } + + // If attack is set to only hit + // one entity at a time (attack_one), + // we verify last hit (lasthit) is + // set. If last hit is set and + // differs from current target, + // then we are trying to hit + // another entity and should exit. + if(current_anim->attack_one) { + if(attacking_entity->lasthit) { + if(target != attacking_entity->lasthit) { + continue; + } + } + } + + /* + * Verify this is a faction we + * can hit. + */ + if (!faction_check_can_damage(attacking_entity, target, indirect)) { + continue; + } + + /* + * Pain time must have expired. + * This is to allow reasonable delay + * between hits so engine will not + * run hit on every update. + */ + if(target->next_hit_time >= _time) { + continue; + } + + // Target takedamage flag + // must be set. + if(!target->takedamage) { + continue; + } + + /* + * Avoid mutiple hits per update for a single collision. + * If any last incoming attack IDs match current attack ID + * then we exit iteration. + * + * Note that function includes exceptions for an attacks + * that ignore IDs and the global mutlihit cheat. + */ + const bool multihit_enabled = (global_config.cheats & CHEAT_OPTIONS_MULTIHIT_ACTIVE) != 0; + const bool attack_id_match = attack_id_check_match(target, attack, current_attack_id, multihit_enabled); + + if(attack_id_match) { + continue; + } + + // Target laying down? Exit if + // attack only hits standing targets. + // Otherwise exit if attack only hits + // grounded targets. + if(target->takeaction == common_lie) { + if(attack->otg == OTG_NONE) { + continue; + } + + } else { + if(attack->otg == OTG_GROUND_ONLY) { + continue; + } + } + + //printf("\n\n Check"); + + if(inair(target)) { + //printf("\n\n In air. \n\t Jugglecost: %d \n\t Jugglepoints: %d", attack->jugglecost, target->jugglepoints); + + if(attack->jugglecost > target->jugglepoints) { + //printf("\n\n Continue."); + continue; + } + } + + // Execute the doattack scripts so author can set take action + // before the hit code below does. + execute_ondoattack_script(target, attacking_entity, attack, EXCHANGE_RECIPIANT, current_attack_id); + execute_ondoattack_script(attacking_entity, target, attack, EXCHANGE_CONFERRER, current_attack_id); + + // 2010-12-31 + // Damon V. Caskey + + // If lasthit.confirm is not true, it must have been turned off by the author; almost + // certainly with the ondoattack event scripts above. Skip the engine's + // default hit handling below. Useful for scripting parry systems, alternate blocking, + // or other custom collision events. + if(lasthit.confirm) { + didhit = true; + + } else { + continue; + } + + otherowner = target; // trace top owner for opponent + while(otherowner->owner) { + otherowner = otherowner->owner; + } + + // Ensure projectile can't hit its owner. Important since projectile attack + // boxes usualy overlap owner's body boxes as the projectile is first thrown. + // Note if an author wants to add projectile reflection feature, they need to + // change the projectile's owner when reflect effect occurs, or the reflected + // projectile won't be able to hit its original owner. If they need to know the + // original owner after changing the owner property, they can check the parent + // property. + if(topowner == otherowner) { + didhit = false; + } + + //Ground missle checking, and bullets wont hit each other + + const bool both_owners_exist = attacking_entity->owner && target->owner; + const bool ground_attack_is_airborne = attacking_entity->modeldata.ground && inair(attacking_entity); + + if(both_owners_exist || ground_attack_is_airborne) { + didhit = false; + } + + // Blocking code section. + if(didhit) { + if(attack->attack_type == ATK_ITEM) { + do_item_script(target, attacking_entity); + + do_attack_invoke_didfind_item(target, attacking_entity); + return; + } + + /* + * Set bomb projectile to detonate status if it + * hits. Set to detonate if it takes a hit and + * has the "take hit" detonate animation. + * + * Bombs that don't have detonate animation take + * hits normally. + */ + + if(target->toexplode & EXPLODE_PREPARE_TOUCH) { + if (validanim(target, ANI_ATTACK2)) { + target->toexplode |= EXPLODE_DETONATE_DAMAGED; + } + } + + if(attacking_entity->toexplode & EXPLODE_PREPARE_TOUCH) { + attacking_entity->toexplode |= EXPLODE_DETONATE_HIT; + } + + /* + * Reduce available juggle points. + */ + if(inair(target)) { + target->jugglepoints -= attack->jugglecost; + } + + block_state = check_blocking_master(target, attacking_entity, attack, target_body_object); + didblock = (block_state & BLOCK_STATE_ACTIVE) != 0; + + // Blocking the attack? + if(didblock) { + // Perform the blocking actions. + do_passive_block(target, attacking_entity, attack, block_state); + + } else if(try_counter_action(target, attacking_entity, attack, target_body_object)) { // Counter action? + + /* Kratus(20 - 04 - 21) used by the multihit glitch memorization. */ + attack_update_id(target, current_attack_id); + + } else if(target->takedamage(target, attacking_entity, attack, 0, defense_object)) { + + + // This is the code block for normal hits. + // The hit was not blocked, countered, or + // otherwise nullified, and this entity + // has takedamage() function. Let's + // process the hit. + + execute_didhit_script(attacking_entity, target, attack, 0); + ++attacking_entity->animation->hit_count; + + attacking_entity->lasthit = target; + + // Flash spawn. + spawn_attack_flash(target, attack, attack->flash.model_hit, target->modeldata.flash.model_hit); + + // Add to owner's combo time. + topowner->combotime = _time + combodelay; + + // If equalairpause is set, inair(attacking_entity) is nolonger a condition for extra pausetime. + if(attacking_entity->pausetime < _time || (inair(attacking_entity) && !equalairpause)) { + // Adds pause to the current animation + attacking_entity->toss_time += attack->pause_add; // So jump height pauses in midair + attacking_entity->nextmove += attack->pause_add; // xdir, zdir + + attacking_entity->nextanim = animation_timestamp_add_bounded( + attacking_entity->nextanim, + attack->pause_add + ); + + attacking_entity->nextthink += attack->pause_add; // So anything that auto moves will pause + attacking_entity->pausetime = _time + attack->pause_add ; //UT: temporary solution + } + + target->toss_time += attack->pause_add; // So jump height pauses in midair + target->nextmove += attack->pause_add; // xdir, zdir + target->nextanim = animation_timestamp_add_bounded( + target->nextanim, + attack->pause_add + ); // Pause animation for a bit. + target->nextthink += attack->pause_add; // So anything that auto moves will pause + + } else { + // If we made it to this block the hit was + // not countered or blocked, but the entity + // does not have a takedamage() function. It + // therefore must be a type that is meant + // to ignore hits. + + didhit = false; + continue; + } + + // 2007 3 24, hmm, def should be like this + if(didblock && !def) { + def = target; + } + + // Attacker executes a follow up animation if it can. + try_follow_up(attacking_entity, target, attacking_entity->animation, didblock); + + /* Kratus(20 - 04 - 21) used by the multihit glitch memorization. */ + attack_update_id(target, current_attack_id); + + /* + * Utunnels + * 2011-11-24 UT + * + * Move the next_hit_time logic here, because block needs this + * as well. Otherwise, blockratio causes instant death + */ + target->next_hit_time = _time + (attack->next_hit_time ? attack->next_hit_time : (global_config.game_speed / 5)); + target->nextattack = 0; // reset this, make it easier to fight back + } + + } + + + // Did we get a hit? let's process it. + if(didhit) { + // Handle energy cost if attacking animation has any. + + // Caskey, Damon V. + // 2020-01-22 + // + // I'm honestly not sure how the legacy logic works. Will need to spend + // some more time breaking it down. + + if(current_anim->energy_cost.cost > 0) { + + // well, dont check player or not - UTunnels. TODO: take care of that health cheat + if(attacking_entity == topowner && nocost && !(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE)) { + attacking_entity->tocost = 1; // Set flag so life is subtracted when animation is finished + + } else if(attacking_entity != topowner && nocost && !(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE) && !attacking_entity->tocost) { // if it is not top, then must be a shot. + + if(current_anim->energy_cost.mponly != COST_TYPE_MP_THEN_HP && topowner->energy_state.mp_current > 0) { + + topowner->energy_state.mp_current -= current_anim->energy_cost.cost; + if(topowner->energy_state.mp_current < 0) { + topowner->energy_state.mp_current = 0; + } + + } else { + topowner->energy_state.health_current -= current_anim->energy_cost.cost; + + if(topowner->energy_state.health_current <= 0) { + topowner->energy_state.health_current = 1; + } + } + + // Little backwards, but set to 1 so cost doesn't get subtracted multiple times. + attacking_entity->tocost = 1; + } + } + + /* + * Caskey, Damon V. + * 2008-04-27 + * + * Added checks for defense property specific blockratio and type. + * Could probably use some cleaning. + */ + if(didblock && level->nohurt == DAMAGE_FROM_ENEMY_ON) { + + /* Gets total force after offense and defense adjustments. */ + force = calculate_force_damage(def, attacking_entity, attack, defense_object, true); + + /* + * Handle block type. Global blocktype is a + * legacy true/false set by "mpblock", so If + * our defense object defaults to global we + * will need to get the correct constant + * before we run downstream switch statement. + * + * True = MP then HP. + * False = HP. + */ + + if (defense_object->blocktype == BLOCK_TYPE_GLOBAL) { + blocktype = global_config.block_type ? BLOCK_TYPE_MP_FIRST : BLOCK_TYPE_HP; + + } else { + blocktype = defense_object->blocktype; + } + + switch (blocktype) { + case BLOCK_TYPE_GLOBAL: + case BLOCK_TYPE_HP: + /* + * Do nothing. This allows creators to overidde energy_cost + * mponly 1 with health only. + */ + break; + + case BLOCK_TYPE_MP_ONLY: + + def->energy_state.mp_current -= force; + force = 0; + + if(def->energy_state.mp_current < 0) { + def->energy_state.mp_current = 0; + } + + break; + + case BLOCK_TYPE_MP_FIRST: + + def->energy_state.mp_current -= force; + + /* + * If there isn't enough MP to cover force, subtract remaining + * MP from force and set MP to 0. + */ + if(def->energy_state.mp_current < 0) { + force = -def->energy_state.mp_current; + def->energy_state.mp_current = 0; + + } else { + force = 0; + } + + break; + + case BLOCK_TYPE_BOTH: + + def->energy_state.mp_current -= force; + + if(def->energy_state.mp_current < 0) { + def->energy_state.mp_current = 0; + } + + break; + } + + // Apply remaining damage force to HP after blocking calculations. + // + // 1. Damage force < HP: Subtract directly - Don't use take + // damage because we don't want a visible reaction in game. + // + // 2. Damage force > HP, but nochipdeath is enabled (meaning + // chip death is not allowed) - Set HP to 1. Again, we don't + // want a reaction. + // + // 3. Damage force > HP, chip death is allowed - Set take + // damage so engine will apply damage normally and KO the + // entity. + if(force < def->energy_state.health_current) { + def->energy_state.health_current -= force; + } else if(nochipdeath) { + def->energy_state.health_current = 1; + } else { + def->takedamage(def, attacking_entity, attack, 0, defense_object); + } + } + + // If the attack was not blocked, let's increment the + // attacker's combo counter and time. + if(!didblock) { + + topowner->rush.time = _time + (global_config.game_speed * rush[1]); + topowner->rush.count++; + + if(topowner->rush.count > topowner->rush.max && topowner->rush.count > 1) { + topowner->rush.max = topowner->rush.count; + } + } + + /* Choose the sound and playback speed for hit. */ + play_hit_impact_sound(attack, attacking_entity, didblock); + + /* + * If the auto kill flag is set, attacker + * kills itself instantly. Used mainly for + * projectiles. + */ + if(attacking_entity->autokill & AUTOKILL_ATTACK_HIT) { + kill_entity(attacking_entity, KILL_ENTITY_TRIGGER_AUTOKILL_ATTACK_HIT); + } + } +#undef followed +} + +/* +* Caskey, Damon V +* 2021-11-16 +* +* Play appropriate hit impact sound +* based on hit/block, attack force, +* and defined samples. +*/ +int play_hit_impact_sound(s_attack* attack_object, entity* attacking_entity, int attack_blocked) +{ +#define PLAYBACK_PRIORITY 0 +#define PLAYBACK_SPEED_MIN 60 +#define PLAYBACK_SPEED_MAX 100 +#define PLAYBACK_SPEED_MODIFIER 5 + + int sound_index = SAMPLE_ID_NONE; + int playback_speed = PLAYBACK_SPEED_MAX; + + /* + * Play the impact sound effect. + * + * 1. Attack blocked. + * a) Attack has block sound - play attack block sound. + * b) Attack does not have block sound - play global block sound. + * + * 2. Attacker is actually a "projectile" (as in, thrown or knocked + * through the air by a blasting attack) - play global indirect sound. + * + * 3. Typical attack hit. + * a) Attack has hit sound - play attack hit sound. + * b) Attack does not have hit sound - play global hit sound. + */ + + if (attack_blocked) + { + if (attack_object->blocksound >= 0) + { + sound_index = attack_object->blocksound; + } + else if (global_sample_list.block >= 0) + { + sound_index = global_sample_list.block; + } + } + else if (attacking_entity->projectile != BLAST_NONE && global_sample_list.indirect >= 0) + { + sound_index = global_sample_list.indirect; + } + else if (attack_object->hitsound >= 0) + { + sound_index = attack_object->hitsound; + + /* + * If noslofx is enabled, then just play sound at default speed. + * Otherwise, subtract 5 from damage force, and then subtract + * that result from default play speed. + * + * This has the effect of slowing down the sound playback as damage + * increases to give it a slightly lower tone and more impact. + */ + + playback_speed = PLAYBACK_SPEED_MAX - (noslowfx ? 0 : (attack_object->attack_force - PLAYBACK_SPEED_MODIFIER)); + + if (playback_speed > PLAYBACK_SPEED_MAX) + { + playback_speed = PLAYBACK_SPEED_MAX; + } + else if (playback_speed < PLAYBACK_SPEED_MIN) + { + playback_speed = PLAYBACK_SPEED_MIN; + } + } + + if (sound_index != SAMPLE_ID_NONE) + { + sound_play_sample(sound_index, PLAYBACK_PRIORITY, savedata.effectvol, savedata.effectvol, playback_speed); + } + + return sound_index; + +#undef PLAYBACK_PRIORITY +#undef PLAYBACK_SPEED_MIN +#undef PLAYBACK_SPEED_MAX +#undef PLAYBACK_SPEED_MODIFIER +} + +// it can be useful for next changes +/*static int is_obstacle_around(entity* ent, float threshold) +{ + int i, j; + int heightvar; + + if(ent->animation->size.y) + { + heightvar = ent->animation->size.y; + } + else + { + heightvar = ent->modeldata.size.y; + } + + for(i = -1; i <= 1; i++ ) + { + for(j = -1; j <= 1; j++ ) + { + int w; + entity *hito = find_ent_here(ent, ent->position.x+(i*threshold), ent->position.z+(j*threshold), (TYPE_OBSTACLE | TYPE_TRAP), NULL); + entity *hitp = check_platform_between(ent->position.x+(i*threshold), ent->position.z+(j*threshold), ent->position.y, ent->position.y + heightvar, ent); + int hitw = (int)( (w = checkwall_below(ent->position.x+(i*threshold), ent->position.z+(j*threshold), T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[w].height > ent->position.y ); + + if ( hito || hitp || hitw ) return 1; + } + } + + return 0; +}*/ + +// Caskey, Damon V. +// 2018-04-20 +// +// Go to landing frame if available. Also spawns an effect ("dust") entity if set. +bool check_landframe(entity *ent) +{ + entity *effect; + + s_onframe_set* land; + + land = &ent->animation->landframe; + + // Must have a landframe. + if(land->frame == FRAME_NONE) + { + return 0; + } + + // Can't be bound with a landframe override. + if(check_bind_override(ent, BIND_CONFIG_OVERRIDE_LANDFRAME)) + { + return 0; + } + + // Can't be passed over current animation's frame count. + if(land->frame > ent->animation->numframes) + { + return 0; + } + + // Can't be already at or passed land frame. + if(ent->animpos >= land->frame) + { + return 0; + } + + // If a land frame dust effect entity is set, let's spawn it here. + if(land->model_index >= 0) + { + effect = spawn(ent->position.x, ent->position.z, ent->position.y, ent->direction, NULL, land->model_index, NULL); + + if(effect) + { + effect->spawntype = SPAWN_TYPE_DUST_LAND; + effect->base = ent->position.y; + effect->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(effect); + } + } + + update_frame(ent, land->frame); + + return 1; +} + +// Caskey, Damon V. +// 2018-04-20 +// +// Go to landing frame if available. Also spawns an effect ("dust") entity if set. +bool check_frame_set_drop(entity* ent) +{ + entity* effect; + + s_onframe_set* drop; + + drop = &ent->animation->dropframe; + + // Dropframe set? + if (drop->frame == FRAME_NONE) + { + return 0; + } + + // Falling? + if (ent->velocity.y > 0) + { + return 0; + } + + /* Can't be bound with a drop frame override. */ + if (check_bind_override(ent, BIND_CONFIG_OVERRIDE_DROPFRAME)) + { + return 0; + } + + // Can't be passed over current animation's frame count. + if (drop->frame > ent->animation->numframes) + { + return 0; + } + + // Can't be already at or passed drop frame. + if (ent->animpos >= drop->frame) + { + return 0; + } + + // Passed all checks. Let's update frame and spawn model (if available). + + update_frame(ent, drop->frame); + + // If a frame set effect entity is set, let's spawn it here. + if (drop->model_index != MODEL_INDEX_NONE) + { + effect = spawn(ent->position.x, ent->position.z, ent->position.y, ent->direction, NULL, drop->model_index, NULL); + + if (effect) + { + effect->spawntype = SPAWN_TYPE_DUST_DROP; + effect->base = ent->position.y; + effect->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(effect); + } + } + + return 1; +} + + +int check_edge(entity *ent) +{ + float x = ent->position.x; + float z = ent->position.z; + float y = ent->position.y; + e_direction dir = ent->direction; + float height = 0.0f; + float t_alt = 1.0f, t_walkoff = 1.0f, t_edge_default = 8.0f; + float t_edge_x = t_edge_default, t_edge_z = t_edge_default / 2; + + if (ent->modeldata.edgerange.x > t_edge_x) t_edge_x = ent->modeldata.edgerange.x; + if (ent->modeldata.edgerange.z > t_edge_z) t_edge_z = ent->modeldata.edgerange.z; + + if(ent->animation->size.y) height = ent->animation->size.y; + else height = ent->modeldata.size.y; + + //test base + float base_left = checkbase(x - t_edge_x, z, y + t_walkoff, ent); + float base_right = checkbase(x + t_edge_x, z, y + t_walkoff, ent); + float base_up = checkbase(x, z - t_edge_z, y + t_walkoff, ent); + float base_down = checkbase(x, z + t_edge_z, y + t_walkoff, ent); + + entity *plat_left = check_platform_between(x - t_edge_x, z, y + t_walkoff, y + height, ent); + entity *plat_right = check_platform_between(x + t_edge_x, z, y + t_walkoff, y + height, ent); + entity *plat_up = check_platform_between(x, z - t_edge_z, y + t_walkoff, y + height, ent); + entity *plat_down = check_platform_between(x, z + t_edge_z, y + t_walkoff, y + height, ent); + + if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT; + else if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT; + + // priority based on direction check + if (dir == DIRECTION_LEFT) + { + if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT; + else if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT; + } + else + { + if ( (base_right < y - t_alt) && plat_right == NULL ) return EDGE_RIGHT; + else if ( (base_left < y - t_alt) && plat_left == NULL ) return EDGE_LEFT; + } + + if ( + ((base_up < y - t_alt) && plat_up == NULL) || + ((base_down < y - t_alt) && plat_down == NULL) + ) return EDGE_LEFT + EDGE_RIGHT; + + return EDGE_NONE; +} + +void check_gravity(entity *e) +{ + + int heightvar; + entity *other = NULL, *dust, *tempself, *plat = NULL; + float gravity; + float fmin, fmax; + + if(e->update_mark & UPDATE_MARK_CHECK_GRAVITY) + { + return; + } + + tempself = self; + self = e; + + adjust_base(self, &plat); + if (self->position.y <= self->base) self->edge = check_edge(self); // && self->idling & IDLING_ACTIVE + else self->edge = EDGE_NONE; + + if(!is_frozen(self) )// Incase an entity is in the air, don't update animations + { + if(self->animation->size.y) + { + heightvar = self->animation->size.y; + } + else + { + heightvar = self->modeldata.size.y; + } + + // White Dragon: turn-off the hitwall flag if you're not near a obstacle. this help to avoid a hit loop + /*if( (self->position.y <= self->base || !inair(self)) && self->velocity.y <= 0) + { + if ( self->hitwall && !is_obstacle_around(self,1.0) ) self->hitwall = 0; + }*/ + + if((self->falling || self->velocity.y || self->position.y != self->base) && self->toss_time <= _time) + { + if(heightvar && self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM && self->velocity.y > 0) + { + other = check_platform_above_entity(self); + } + else + { + other = NULL; + } + + if( other && other->position.y <= self->position.y + heightvar && !(other->modeldata.move_config_flags & MOVE_CONFIG_NO_HIT_HEAD)) + { + if(self->hithead == NULL) // bang! Hit the ceiling. + { + self->velocity.y = 0; + self->hithead = other; + execute_onblocky_script(self, other); + } + } + else + { + self->hithead = NULL; + } + + // gravity, antigravity factors + self->position.y += self->velocity.y * 100.0 / global_config.game_speed; + if(!(self->animation->move_config_flags & MOVE_CONFIG_SUBJECT_TO_GRAVITY)) + { + gravity = 0; + } + else + { + gravity = (level ? level->gravity : default_level_gravity) * (1.0 - self->modeldata.antigravity); + } + + if(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_GRAVITY) + { + self->velocity.y += gravity * 100.0 / global_config.game_speed; + } + + fmin = (level ? level->maxfallspeed : default_level_maxfallspeed); + fmax = (level ? level->maxtossspeed : default_level_maxtossspeed); + + if(self->velocity.y < fmin) + { + self->velocity.y = fmin; + } + else if(self->velocity.y > fmax) + { + self->velocity.y = fmax; + } + + // Evaluate and apply drop frame settings if we have them. + check_frame_set_drop(self); + + if (self->velocity.y) + { + execute_onmovea_script(self); //Move A event. + } + + if( self->idling && validanim(self, ANI_WALKOFF) && diff(self->position.y, self->base) > T_WALKOFF ) + { + entity *cplat = check_platform_below(self->position.x,self->position.z-1.0,self->position.y,self); + + // White Dragon: fix for too low velocityz + if ( !cplat || (cplat && diff(get_platform_base(cplat),self->position.y) > T_WALKOFF) ) + { + self->idling = IDLING_NONE; + self->ducking = DUCK_NONE; + self->takeaction = common_walkoff; + ent_set_anim(self, ANI_WALKOFF, 0); + self->landed_on_platform = plat = NULL; + } + } + + // UTunnels: tossv <= 0 means land, while >0 means still rising, so + // you wont be stopped if you are passing the edge of a wall + if( self->position.y <= self->base || !inair(self)) + { + // 0+ means still rising. We need to be falling. + if(self->velocity.y <=0) + { + // No bind target, or binding set to ignore fall lands. + if(!check_bind_override(self, BIND_CONFIG_OVERRIDE_FALL_LAND)) + { + self->position.y = self->base; + self->falling = 0; + + if ( self->hitwall ) self->hitwall = 0; + + // cust dust entity + if(self->modeldata.dust.fall_land >= 0 && self->velocity.y < -1 && self->drop) + { + dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.fall_land, NULL); + if(dust) + { + dust->spawntype = SPAWN_TYPE_DUST_FALL; + dust->base = self->position.y; + dust->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(dust); + } + } + + // bounce/quake + if(tobounce(self) && self->modeldata.bounce) + { + int i; + self->velocity.x /= self->animation->bounce_factor; + self->velocity.z /= self->animation->bounce_factor; + toss(self, (-self->velocity.y) / self->animation->bounce_factor); + if(level && !(self->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SCREEN)) + { + level->quake = 4; // Don't shake if specified + } + if(global_sample_list.fall >= 0) + { + sound_play_sample(global_sample_list.fall, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(self->modeldata.type & TYPE_PLAYER) + { + if (savedata.joyrumble[self->playerindex]) control_rumble(self->playerindex, 1, 100 * (int)self->velocity.y / 2); + } + for(i = 0; i < MAX_PLAYERS; i++) + { + if (savedata.joyrumble[i]) control_rumble(i, 1, 75 * (int)self->velocity.y / 2); + } + } + else if((!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) && + (!self->animation->move[self->animpos]->axis.y || self->animation->move[self->animpos]->axis.y <= 0)) + { + self->velocity.x = 0; + self->velocity.z = 0; + self->velocity.y = 0; + } + else + { + self->velocity.y = 0; + } + + if(plat && !self->landed_on_platform && self->position.y <= plat->position.y + plat->animation->platform[plat->animpos][PLATFORM_HEIGHT]) + { + self->landed_on_platform = plat; + } + + // Set landing frame if we have one. + check_landframe(self); + + // Taking damage on a landing? + // Kratus (01-2024) Now the damage_on_landing is affected by infinite health cheat + // Kratus (02-2024) Minor fix to make health cheat affect players only + if(((self->modeldata.type & TYPE_PLAYER) && !(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE))|| + !(self->modeldata.type & TYPE_PLAYER)) + { + checkdamageonlanding(self); + } + + // in case landing, set hithead to NULL + self->hithead = NULL; + } + } + }// end of if - land checking + }// end of if - in-air checking + + if(self->toss_time <= _time) + { + self->toss_time = _time + 1; + } + + }//end of if + + self->update_mark |= UPDATE_MARK_CHECK_GRAVITY; + + self = tempself; +} + +bool check_lost() { + + s_attack attack = emptyattack; + int osk = self->modeldata.offscreenkill ? self->modeldata.offscreenkill : DEFAULT_OFFSCREEN_KILL; + + if((self->position.z != ITEM_HIDE_POSITION_Z && (advancex - self->position.x > osk || self->position.x - advancex - videomodes.hRes > osk || + (level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN && (advancey - self->position.z + self->position.y > osk || self->position.z - self->position.y - advancey - videomodes.vRes > osk)) || + ((level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) && (self->position.z - self->position.y < -osk || self->position.z - self->position.y > videomodes.vRes + osk)) ) ) + || self->position.y < 2 * PIT_DEPTH) { + + if(self->modeldata.type & TYPE_PLAYER) { + player_die(); + + } else { + kill_entity(self, KILL_ENTITY_TRIGGER_OUT_OF_BOUNDS); + } + return true; + } + + const s_defense* defense_object = defense_find_current_object(self, NULL, attack.attack_type); + + // fall into a pit + if(self->position.y < PIT_DEPTH) { + + if(!self->takedamage) { + kill_entity(self, KILL_ENTITY_TRIGGER_PIT); + + } else { + attack.dropv = default_model_dropv; + attack.attack_force = self->energy_state.health_current; + attack.attack_type = ATK_PIT; + + self->takedamage(self, self, &attack, 0, defense_object); + } + return true; + + } else if(self->lifespancountdown < 0) { //Lifespan expired. + + if(!self->takedamage) { + kill_entity(self, KILL_ENTITY_TRIGGER_LIFESPAN); + + } else { + attack.dropv = default_model_dropv; + attack.attack_force = self->energy_state.health_current; + attack.attack_type = ATK_LIFESPAN; + self->takedamage(self, self, &attack, 0, defense_object); + } + return true; + } + + // Doom count down + if(!is_frozen(self) && self->lifespancountdown != LIFESPAN_DEFAULT) { + self->lifespancountdown--; + } + + return false; +} + +// grab walk check +void check_link_move(float xdir, float zdir) +{ + float x, z, gx, gz; + int tryresult; + entity *tempself = self; + + gx = self->grabbing->position.x; + gz = self->grabbing->position.z; + x = self->position.x; + z = self->position.z; + self = self->grabbing; + tryresult = self->trymove(xdir, zdir); + self = tempself; + + if(tryresult != 1) // changed + { + xdir = self->grabbing->position.x - gx; + zdir = self->grabbing->position.z - gz; + } + tryresult = self->trymove(xdir, zdir); + if(tryresult != 1) + { + self->grabbing->position.x = self->position.x - x + gx; + self->grabbing->position.z = self->position.z - z + gz; + } +} + +/* +* Caskey, Damon V. +* 2022-04-21 +* +* Return true if currently in any +* active game or menu screen. +*/ +int check_in_screen() +{ + /* + * Check the screen status first. + * It should be faster. + */ + + if (screen_status & (IN_SCREEN_BUTTON_CONFIG_MENU | IN_SCREEN_SELECT | IN_SCREEN_TITLE | IN_SCREEN_HALL_OF_FAME | IN_SCREEN_GAME_OVER | IN_SCREEN_SHOW_COMPLETE | IN_SCREEN_SHOW_GO_ARROW | IN_SCREEN_ENGINE_CREDIT | IN_SCREEN_MENU | IN_SCREEN_GAME_START_MENU | IN_SCREEN_NEW_GAME_MENU | IN_SCREEN_LOAD_GAME_MENU | IN_SCREEN_OPTIONS_MENU | IN_SCREEN_CONTROL_OPTIONS_MENU | IN_SCREEN_SOUND_OPTIONS_MENU | IN_SCREEN_VIDEO_OPTIONS_MENU | IN_SCREEN_SYSTEM_OPTIONS_MENU)) + { + return 1; + } + + if (currentScene) + { + return 1; + } + + return 0; + +} + +void check_ai() +{ + if(self->nextthink <= _time && !endgame) + { + self->update_mark |= UPDATE_MARK_CHECK_AI; //mark it + + // take actions + if(self->takeaction) + { + self->takeaction(); + } + + // A.I. think + if(self->think) + { + if(self->nextthink <= _time) + { + self->nextthink = _time + THINK_SPEED; + } + // use noaicontrol flag to turn of A.I. think + if(!self->noaicontrol) + { + self->think(); + } + } + + // Execute think script + execute_think_script(self); + + // Used so all entities can have a spawn animation, and then just changes to the idle animation when done + // move here to so players wont get stuck + if((self->animation == self->modeldata.animation[ANI_SPAWN] || self->animation == self->modeldata.animation[ANI_RESPAWN]) && !self->animating /*&& (!inair(self)||!(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_GRAVITY))*/) + { + set_idle(self); + } + } +} + +float check_basemap(int x, int z) +{ + float maxbase = 0, base = T_MIN_BASEMAP; + int i; + + if(!level) + { + return 0; + } + + for(i = 0; i < level->numbasemaps; i++) + { + if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x && + z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z) + { + base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)]; + if(base > maxbase) + { + maxbase = base; + } + } + } + return base == T_MIN_BASEMAP ? base : maxbase; +} + +int check_basemap_index(int x, int z) +{ + float maxbase = 0, base = T_MIN_BASEMAP; + int i, index = -1; + + if(!level) + { + return -1; + } + + for(i = 0; i < level->numbasemaps; i++) + { + if(x >= level->basemaps[i].position.x && x < level->basemaps[i].position.x + level->basemaps[i].size.x && + z >= level->basemaps[i].position.z && z < level->basemaps[i].position.z + level->basemaps[i].size.z) + { + base = level->basemaps[i].map[x - level->basemaps[i].position.x + level->basemaps[i].size.x * (z - level->basemaps[i].position.z)]; + if(base > maxbase) + { + maxbase = base; + index = i; + } + } + } + return base == T_MIN_BASEMAP ? -1 : index; +} + + + +void adjust_base(entity *acting_entity, entity **pla) +{ + int wall = WALL_INDEX_NONE; + int in_hole = 0; + int hole_index = HOLE_INDEX_NONE; + float seta = 0; + float maxbase = 0; + float base_adjust = 0.0; + entity* other = NULL; + entity* platform = NULL; + + if(acting_entity->velocity.y > 0) + { + acting_entity->landed_on_platform = NULL; + } + + /* + * If no platform found below entity + * then clear our local platform and + * the platform argument. + * + * If entity isn't subject to platform + * at all, then clear entity's last + * landed platform, platform parameter, + * and the local other entity. + */ + if(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM) + { + other = check_platform_below_entity(acting_entity); + if(!other && acting_entity->landed_on_platform) + { + *pla = NULL; + acting_entity->landed_on_platform = NULL; + } + } + else + { + *pla = NULL; + other = NULL; + acting_entity->landed_on_platform = NULL; + } + + if(other && !(other->update_mark & UPDATE_MARK_CHECK_GRAVITY)) + { + check_gravity(other); + } + + //no longer underneath? + if(acting_entity->landed_on_platform && !testplatform(acting_entity->landed_on_platform, acting_entity->position.x, acting_entity->position.z, NULL)) + { + *pla = acting_entity->landed_on_platform = NULL; + } + + if(other + && !acting_entity->landed_on_platform + && acting_entity->position.y <= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]) + { + acting_entity->landed_on_platform = other; + } + + if( (platform = acting_entity->landed_on_platform) ) + { + if(!(platform->update_mark & UPDATE_MARK_CHECK_GRAVITY)) + { + check_gravity(platform); + } + acting_entity->position.y = acting_entity->base = platform->position.y + platform->animation->platform[platform->animpos][PLATFORM_HEIGHT]; + } + + *pla = other; + + // adjust base + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_ADJUST_BASE) + { + return; + } + + if (acting_entity->animation->move[acting_entity->animpos]->base) + { + seta = (float)acting_entity->animation->move[acting_entity->animpos]->base; + } + else + { + seta = -1; + } + + // Checks to see if entity is over a wall and or obstacle, and adjusts the base accordingly + //wall = checkwall_below(acting_entity->position.x, acting_entity->position.z); + //find a wall below us + if(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_WALL) + { + wall = checkwall_below(acting_entity->position.x, acting_entity->position.z, T_MAX_CHECK_ALTITUDE); + } + else + { + wall = WALL_INDEX_NONE; + } + /* + * If we can't adjust base, nothing downstream + * matters. Just exit. + */ + + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_BASEMAP) + { + maxbase = check_basemap(acting_entity->position.x, acting_entity->position.z); + } + + /* + * Fall into a hole? + */ + + if(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_HOLE && + ((acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_BASEMAP && maxbase == T_MIN_BASEMAP) || !(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_BASEMAP)) + ) + { + in_hole = (wall < 0 && !other) ? checkhole_in(acting_entity->position.x, acting_entity->position.z, acting_entity->position.y) : 0; + + if (in_hole) + { + hole_index = checkholeindex_in(acting_entity->position.x, acting_entity->position.z, acting_entity->position.y); + + if (hole_index >= 0) + { + execute_inhole_script(acting_entity, &level->holes[hole_index], hole_index); + } + } + + if(seta < 0 && in_hole) + { + acting_entity->base = T_MIN_BASEMAP; + ent_unlink(acting_entity); + } + else if(!in_hole && acting_entity->base == T_MIN_BASEMAP) + { + if(acting_entity->position.y >= 0) + { + acting_entity->base = 0; + } + else + { + acting_entity->velocity.x = acting_entity->velocity.z = 0; // hit the hole border + } + } + } + + /* + * Apply platform, wall, and manual base + * adjustments. + */ + + if(acting_entity->base != T_MIN_BASEMAP || wall >= 0) + { + base_adjust = (seta + acting_entity->altbase >= 0) * (seta + acting_entity->altbase); + + /* + * If (wall > position Y) + * Explode. + * Else + * Adjust base. + */ + if(other != NULL && other != acting_entity) + { + acting_entity->base = base_adjust + other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]; + } + else if(wall >= 0) + { + /* + * Bombs normally need to adjust base to control + * detonation when landing, but this has a side + * effect: Bombs that hit the side of walls or + * platforms instantly warp to the top of wall + * as they detonate. + * + * Legacy projectile wall bounce has a similar + * issue. + * + * To fix this, we check to see if wall is higher + * than current position of entities with their + * detonation flags on. If so, then reset entity + * base. + */ + + if ((acting_entity->toexplode & (EXPLODE_PREPARE_GROUND | EXPLODE_DETONATE_HIT) || acting_entity->modeldata.move_config_flags & MOVE_CONFIG_PROJECTILE_WALL_BOUNCE) && level->walls[wall].height > acting_entity->position.y) + { + acting_entity->base = 0; + } + else + { + acting_entity->base = base_adjust + level->walls[wall].height; + } + } + else if(seta >= 0) + { + acting_entity->base = base_adjust; + } + else if(!acting_entity->animation->move[acting_entity->animpos]->axis.y) + { + // No obstacle/wall or seta, so just set to 0 + acting_entity->base = 0; + } + } + + if(acting_entity->base != T_MIN_BASEMAP && maxbase > acting_entity->base) + { + acting_entity->base = maxbase; + // White Dragon: fix bug for floating entity on basemaps using a threshold + if (acting_entity->velocity.y <= 0 && acting_entity->position.y - acting_entity->base <= T_WALKOFF) + { + acting_entity->position.y = acting_entity->base; + } + //debug_printf("y:%f maxbase:%f",acting_entity->position.y,maxbase); + } +} + +// Caskey Damon V. +// 2018-04-08 +// +// If entity has a timed color set, check +// to see if colorset has expired. If so, +// revert to default colorset and clear +// the timer. +int colorset_timed_expire(entity *ent) +{ + // No color set time? Nothing to do. + if(!ent->maptime) + { + return 0; + } + + // If elapsed time has surpassed color + // set time, then color set time is expired. + // Revert entity back to default color set + // and reset the color set timer. + if(_time >= ent->maptime) + { + ent_set_colourmap(ent, ent->map); + ent->maptime = 0; + + return 1; + } + + // Color set _time was not expired. + return 0; +} + + +void update_animation() +{ + int f; + float scrollv = 0; + + if(level) + { + if(self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT) + { + self->direction = DIRECTION_RIGHT; + } + else if(self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_LEFT) + { + self->direction = DIRECTION_LEFT; + } + else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT)) + { + self->direction = DIRECTION_RIGHT; + } + else if((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT)) + { + self->direction = DIRECTION_LEFT; + } + if(self->modeldata.type & TYPE_PANEL) + { + scrollv += self->modeldata.speed.x; + } + if(self->modeldata.scroll) + { + scrollv += self->modeldata.scroll; + } + if(scrollv) + { + self->position.x += scrolldx * scrollv; + if(level->scrolldir == SCROLL_UP) + { + scrollv = -scrollv; + } + self->position.y -= scrolldy * scrollv; + self->base = -99999; // temporary fix otherwise it won't go underground + } + } + + if(!(self->idling & IDLING_ACTIVE) || (self->animnum == ANI_SLEEP && !self->animating)) + { + self->sleeptime = _time + self->modeldata.sleepwait; + } + + // Invincible time has run out, turn off. + if(self->invincible != INVINCIBLE_NONE && _time >= self->invinctime) + { + self->invincible = INVINCIBLE_NONE; + self->blink = 0; + self->invinctime = 0; + self->arrowon = 0; + } + + if(self->freezetime && _time >= self->freezetime) + { + unfrozen(self); + } + + // Check for forced color set expiring. + colorset_timed_expire(self); + + if(self->sealtime && _time >= self->sealtime) //Remove seal, special moves are available again. + { + self->seal = 0; + } + // Reset their escapecount if they aren't being spammed anymore. + if(self->modeldata.escapehits && self->inpain == IN_PAIN_NONE) + { + self->escapecount = 0; + } + + /* + * Cycle frame if the frame time is not infinite + * and the nextanim time has been reached, or if + * the entity is a textbox and the player has + * pressed a key to skip the text. + */ + if((!(self->nextanim & DELAY_FLAG_INFINITE) + && self->nextanim <= _time) || + ((self->modeldata.type & TYPE_TEXTBOX) + && self->modeldata.subtype != SUBTYPE_NOSKIP + && (bothnewkeys + & (FLAG_JUMP + | FLAG_ATTACK + | FLAG_ATTACK2 + | FLAG_ATTACK3 + | FLAG_ATTACK4 + | FLAG_SPECIAL)))) { + + f = self->animpos + self->animating; + + //Specified loop break frame. + if(self->animation->loop.mode && self->animation->loop.frame.max) + { + if (f == self->animation->loop.frame.max) + { + if(f < 0) + { + f = self->animation->numframes - 1; + } + else + { + f = 0; + } + + if (self->animation->loop.frame.min) + { + f = self->animation->loop.frame.min; + } + } + else if((unsigned)f >= (unsigned)self->animation->numframes) + { + self->animating = ANIMATING_NONE; + + if(self->autokill & AUTOKILL_ANIMATION_COMPLETE) + { + kill_entity(self, KILL_ENTITY_TRIGGER_AUTOKILL_ANIMATION_COMPLETE_DEFINED_LOOP_MAX); + return; + } + } + } + else if((unsigned)f >= (unsigned)self->animation->numframes) + { + if(f < 0) + { + f = self->animation->numframes - 1; + } + else + { + f = 0; + } + + if(!self->animation->loop.mode) + { + self->animating = ANIMATING_NONE; + + if(self->autokill & AUTOKILL_ANIMATION_COMPLETE) + { + kill_entity(self, KILL_ENTITY_TRIGGER_AUTOKILL_ANIMATION_COMPLETE_UNDEFINED_LOOP_MAX); + return; + } + } + else + { + if (self->animation->loop.frame.min) + { + f = self->animation->loop.frame.min; + } + } + } + + if(self->animating) + { + //self->nextanim = _time + (self->animation->delay[f]); + self->update_mark |= UPDATE_MARK_UPDATE_ANIMATION; // frame updated, mark it + // just switch frame to f, if frozen, expand_time will deal with it well + update_frame(self, f); + bothnewkeys = 0; //stop mutiple skips. + } + } + +} + +void check_attack(void) { + s_collision_collection* collision_attack = NULL; + int animation_frame; + + /* + * Basic guards. + */ + if (!self || !self->animation) { + return; + } + + /* + * If falling, we don't attack unless + * in a projectile state (as in, thrown + * or blasted by another entity). This + * is how we handle throwing a guy into + * his buddies. + */ + if (self->falling && self->projectile == BLAST_NONE) { + self->attack_id_outgoing = 0; + return; + } + + /* + * On ground after drop. + */ + if (self->drop && !self->falling) { + self->attack_id_outgoing = 0; + return; + } + + /* + * Can't hit an opponent if frozen. + */ + if (is_frozen(self)) { + self->attack_id_outgoing = 0; + return; + } + + animation_frame = self->animpos; + + /* + * Protect animation frame table lookup. + */ + if (animation_frame < 0 || animation_frame >= self->animation->numframes) { + self->attack_id_outgoing = 0; + return; + } + + /* + * Attack collision table must exist, and this frame + * must have at least one active attack slot. + */ + if (self->animation->collision_attack) { + collision_attack = self->animation->collision_attack[animation_frame]; + + if (collision_attack && collision_attack->active_status) { + do_attack(self); + return; + } + } + + self->attack_id_outgoing = 0; +} + +// Caskey, Damon V. +// 2018-04-08 +// +// If actively charging, add energy to +// entity based on chargerate property +// and elapsed time. Returns 1 if +// energy was added, 0 otherwise. +int do_energy_charge(entity *ent) +{ + #define ENERGY_CHARGE_RATE 0.25 + + // Must be charging. + if (!ent->charging) + { + return 0; + } + + // Must be time for a charge tick. + if (_time < ent->mpchargetime) + { + return 0; + } + + // Add charge rate to current mp. + ent->energy_state.mp_current += ent->modeldata.chargerate; + + // Time for next charge tick. + ent->mpchargetime = _time + (global_config.game_speed * ENERGY_CHARGE_RATE); + + return 1; + + #undef ENERGY_CHARGE_RATE +} + + +void update_health() +{ + /* + * 12/30/2008: Guardrate by OX. Guardpoints increase over time. + * + * 2024-01-08, DC: Split max/current between model and + * entity. Remove some redudant code. + * + * Only bother recovering if guardpoints are set in + * the first place (model has guardpoint value > 0). + */ + if(self->modeldata.guardpoints > 0 && _time >= self->guardtime) + { + /* + * Recover guardpoints. Half rate if + * blocking. + */ + if((self->blocking & (BLOCK_STATE_ACTIVE | BLOCK_STATE_NATIVE)) + == (BLOCK_STATE_ACTIVE | BLOCK_STATE_NATIVE) + && !(self->blocking & BLOCK_STATE_IGNORE_GUARD_POINTS)) + { + self->guardpoints += (self->modeldata.guardrate / 2); + } + else + { + self->guardpoints += self->modeldata.guardrate; + } + + /* Cap to maximum. */ + if (self->guardpoints > self->modeldata.guardpoints) + { + self->guardpoints = self->modeldata.guardpoints; + } + + /* Reset guardtime. */ + self->guardtime = _time + global_config.game_speed; + } + + //Damage over time. + recursive_entity_effect_update(self); + + // this is for restoring mp by _time by tails + // Cleaning and addition of mpstable by DC, 08172008. + // stabletype 4 added by OX 12272008 + + if(magic_type == 0 && !self->charging) + { + if(_time >= self->magictime) + { + + // 1 Only recover MP > mpstableval. + // 2 No recover. Drop MP if MP < mpstableval. + // 3 Both: recover if MP < mpstableval and drop if MP > mpstableval. + // 4 Gain until stable, then fall to stable. + // 0 Default. Recover MP at all times. + + if (self->modeldata.mpstable == 1) + { + if (self->energy_state.mp_current < self->modeldata.mpstableval) + { + self->energy_state.mp_current += self->modeldata.mprate; + } + } + else if(self->modeldata.mpstable == 2) + { + if (self->energy_state.mp_current > self->modeldata.mpstableval) + { + self->energy_state.mp_current -= self->modeldata.mpdroprate; + } + } + else if (self->modeldata.mpstable == 3) + { + if (self->energy_state.mp_current < self->modeldata.mpstableval) + { + + self->energy_state.mp_current += self->modeldata.mprate; + } + else if (self->energy_state.mp_current > self->modeldata.mpstableval) + { + self->energy_state.mp_current -= self->modeldata.mpdroprate; + } + } + + // OX. Stabletype 4. Gain mp until it reaches max. Then it drops down to mpstableval. + else if (self->modeldata.mpstable == 4) + { + if(self->energy_state.mp_current <= self->modeldata.mpstableval) + { + self->modeldata.mpswitch = 0; + } + else if(self->energy_state.mp_current == self->modeldata.mp) + { + self->modeldata.mpswitch = 1; + } + + if(self->modeldata.mpswitch == 1) + { + self->energy_state.mp_current -= self->modeldata.mpdroprate; + } + else if(self->modeldata.mpswitch == 0) + { + self->energy_state.mp_current += self->modeldata.mprate; + } + } + else + { + self->energy_state.mp_current += self->modeldata.mprate; + } + + self->magictime = _time + global_config.game_speed; //Reset magictime. + } + } + + // Active MP charging? + do_energy_charge(self); + + /* Energy cheat keeps MP at maximum. */ + if (global_config.cheats & CHEAT_OPTIONS_ENERGY_ACTIVE && self->modeldata.type & TYPE_PLAYER) + { + self->energy_state.mp_current = self->modeldata.mp; + } + + if(self->energy_state.mp_current > self->modeldata.mp) + { + self->energy_state.mp_current = self->modeldata.mp; // Don't want to add more than the max + } + + if(self->energy_state.health_old < self->energy_state.health_current) + { + self->energy_state.health_old++; + } + else if(self->energy_state.health_old > self->energy_state.health_current) + { + self->energy_state.health_old--; + } + + if(self->energy_state.mp_old < self->energy_state.mp_current) + { + self->energy_state.mp_old++; + } + else if(self->energy_state.mp_old > self->energy_state.mp_current) + { + self->energy_state.mp_old--; + } +} + +/* ***** Binding ***** */ + +/* +* Caskey, Damon V. +* 2022-05-27 +* +* Allocate a bind property structure and return pointer. +*/ +s_bind* bind_allocate_object() +{ + s_bind* result; + size_t alloc_size; + + /* Get amount of memory we'll need. */ + alloc_size = sizeof(*result); + + /* Allocate memory and get pointer. */ + result = malloc(alloc_size); + + /* + * Make sure the data members are + * zero'd out. + */ + + memset(result, 0, alloc_size); + + result->object_type = OBJECT_TYPE_BIND; + + return result; +} + +/* +* Caskey, Damon V. +* 2022-05-27 +* +* Allocate new bind object with same values (but not +* same pointers) as received object. Returns pointer +* to new object. +*/ +s_bind* bind_clone_object(s_bind* source) +{ + s_bind* result = NULL; + + if (!source) + { + return result; + } + + result = bind_allocate_object(); + + /* + * Rather than do everything piecemeal, we'll memcopy + * to get all the basic values, and then overwrite + * members individually as needed. + */ + + memcpy(result, source, sizeof(*result)); + + return result; +} + +/* +* Caskey, Damon V +* 2022-05-27 +* +* Send all bind data to log for debugging. +*/ +void bind_dump_object(s_bind* object) { + + printf("\n\n -- Bind (%p) dump --", object); + + if (object) { + printf("\n\t ->animation: %d", object->animation); + printf("\n\t ->config: %" PRIu64, object->config); + printf("\n\t ->direction_adjust: %d", object->direction_adjust); + printf("\n\t ->frame: %d", object->frame); + printf("\n\t ->meta_data: %p", object->meta_data); + printf("\n\t ->meta_tag: %d", object->meta_tag); + printf("\n\t ->offset.x: %d", object->offset.x); + printf("\n\t ->offset.y: %d", object->offset.y); + printf("\n\t ->offset.z: %d", object->offset.z); + printf("\n\t ->sortid: %d", object->sortid); + printf("\n\t ->target: %p", object->target); + } + + printf("\n\n -- Bind (%p) dump complete... -- \n", object); +} + +/* +* Caskey, Damon V. +* 2022-05-27 +* +* Free bind properties from memory. +*/ +void bind_free_object(s_bind* object) +{ + free(object); +} + +/* +* Caskey, Damon V. +* 2022-05-06 +* +* Full rewrite of orginal function by uTunnels +* and its later modifications by me (Damon). +* +* Acting entity binds itself to a target +* depending on bind property settings. Also +* executes bind scripts for acting and target. +*/ +void adjust_bind(entity* acting_entity) { + + #define ADJUST_BIND_SET_ANIM_RESETABLE 1 + #define ADJUST_BIND_NO_FRAME_MATCH -1 + + int frame = 0; + animation_id_t animation = ANI_NONE; + + s_bind *const acting_bind = &acting_entity->binding; + + if (!acting_bind->target) { + return; + } + + execute_on_bind_update_other_to_self(acting_bind->target, acting_entity, acting_bind); + execute_on_bind_update_self_to_other(acting_entity, acting_bind->target, acting_bind); + + if (!acting_bind->target) { + return; + } + + const bind_config_t bind_config = acting_bind->config; + + /* + * Check config flags for removal. Legacy bindentity() + * used values 4 and 8 for animation and frame removal, + * respectively. These values are now defined as + * BIND_CONFIG_ANIMATION_REMOVE and BIND_CONFIG_ANIMATION_FRAME_REMOVE. + * The removal flags imply target matching for legacy + * bindentity() values 4 and 8, even when no explicit + * matching flag is set. + */ + if (bind_config & (BIND_CONFIG_ANIMATION_DEFINED + | BIND_CONFIG_ANIMATION_TARGET + | BIND_CONFIG_ANIMATION_REMOVE + | BIND_CONFIG_ANIMATION_FRAME_DEFINED + | BIND_CONFIG_ANIMATION_FRAME_TARGET + | BIND_CONFIG_ANIMATION_FRAME_REMOVE)) { + + /* + * If a defined value is requested, + * use the binding member value. + * Otherwise use target's current value. + */ + if (bind_config & BIND_CONFIG_ANIMATION_DEFINED) { + animation = acting_bind->animation; + } else { + animation = acting_bind->target->animnum; + } + + /* Are we NOT currently playing the target animation? */ + if (acting_entity->animnum != animation) { + + /* + * If we don't have the target animation + * and animation kill flag is set, then + * we kill ourselves and exit the function. + */ + if (!validanim(acting_entity, animation)) { + + /* Don't have the animation? Kill self. */ + if (bind_config & BIND_CONFIG_ANIMATION_REMOVE) { + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_BIND_ANIMATION_MATCH); + } + + /* Cancel the bind and exit. */ + acting_entity->binding.target = NULL; + return; + } + + /* + * Made it this far, we must have the target + * animation, so let's apply it. + */ + ent_set_anim(acting_entity, animation, ADJUST_BIND_SET_ANIM_RESETABLE); + } + + + /* + * If a defined value is requested, + * use the binding member value. + * If target value is requested use + * target's current value (duh). + * if no frame match at all requested + * then set ADJUST_BIND_NO_FRAME_MATCH + * so frame matching logic is skipped. + */ + + if (bind_config & BIND_CONFIG_ANIMATION_FRAME_DEFINED) { + frame = acting_bind->frame; + } else if (bind_config & BIND_CONFIG_ANIMATION_FRAME_TARGET) { + frame = acting_bind->target->animpos; + } else { + frame = ADJUST_BIND_NO_FRAME_MATCH; + } + + /* + * Any frame match flag set? + */ + if (frame != ADJUST_BIND_NO_FRAME_MATCH) { + + /* Are we NOT currently playing the target frame ? */ + if (acting_entity->animpos != frame) { + + /* + * If we don't have the frame and frame kill flag is + * set, kill self. + */ + if ((acting_entity->animation->numframes -1) < frame) { + if (bind_config & BIND_CONFIG_ANIMATION_FRAME_REMOVE) { + + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_BIND_FRAME_MATCH); + return; + } + } + + /* + * Made it this far, let's try to + * apply the frame. + */ + update_frame(acting_entity, frame); + } + } + } + + /* Apply sort ID adjustment. */ + acting_entity->sortid = acting_bind->target->sortid + acting_bind->sortid; + + /* Getand apply direction adjustment. */ + acting_entity->direction = direction_get_adjustment_result(acting_entity, acting_bind->target, acting_bind->direction_adjust); + + /* + * Apply positioning based on config. For + * the X axis, we invert adjustment when + * target faces left. + */ + + // X + const int offset_x = acting_bind->offset.x; + + if (bind_config & BIND_CONFIG_AXIS_X_TARGET) { + + const float target_x = acting_bind->target->position.x; + + if (acting_bind->target->direction == DIRECTION_LEFT) { + + acting_entity->position.x = target_x - (float)offset_x; + + } else { + + acting_entity->position.x = target_x + (float)offset_x; + + } + + } else if (bind_config & BIND_CONFIG_AXIS_X_LEVEL) { + + acting_entity->position.x = (float)offset_x; + + } + + // Y + const int offset_y = acting_bind->offset.y; + + if (bind_config & BIND_CONFIG_AXIS_Y_TARGET) { + + acting_entity->position.y = acting_bind->target->position.y + (float)offset_y; + + } else if (bind_config & BIND_CONFIG_AXIS_Y_LEVEL) { + + acting_entity->position.y = (float)offset_y; + + } + + // Z + const int offset_z = acting_bind->offset.z; + + if (bind_config & BIND_CONFIG_AXIS_Z_TARGET) { + + acting_entity->position.z = acting_bind->target->position.z + (float)offset_z; + + } else if (bind_config & BIND_CONFIG_AXIS_Z_LEVEL) { + + acting_entity->position.z = (float)offset_z; + + } + + #undef ADJUST_BIND_SET_ANIM_RESETABLE + #undef ADJUST_BIND_NO_FRAME_MATCH +} + +/* +* Caskey, Damon V. +* 2021-08-24 +* +* Read a text argument for direction and output +* appropriate direction constant. If input is +* legacy integer, we just pass it on. +*/ +e_direction direction_get_direction_from_argument(const char* filename, const char* command, const char* value) { + e_direction result = DIRECTION_NONE; + + if (stricmp(value, "left") == 0) { + result = DIRECTION_LEFT; + } else if (stricmp(value, "right") == 0) { + result = DIRECTION_RIGHT; + } else { + /* Just pass through for legacy integer. */ + result = getValidInt(value, filename, command); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2021-08-24 +* +* Read a text argument for direction and output +* appropriate direction adjustment constant. If +* input is legacy integer, we just pass it on. +*/ +e_direction_adjust direction_get_adjustment_from_argument(const char* filename, const char* command, const char* value) { + e_direction_adjust result = DIRECTION_ADJUST_NONE; + + if (stricmp(value, "left") == 0) { + result = DIRECTION_ADJUST_LEFT; + } else if (stricmp(value, "none") == 0) { + result = DIRECTION_ADJUST_NONE; + } else if (stricmp(value, "opposite") == 0) { + result = DIRECTION_ADJUST_OPPOSITE; + } else if (stricmp(value, "right") == 0) { + result = DIRECTION_ADJUST_RIGHT; + } else if (stricmp(value, "same") == 0) { + result = DIRECTION_ADJUST_SAME; + } else if (stricmp(value, "toward") == 0) { + result = DIRECTION_ADJUST_TOWARD; + } else if (stricmp(value, "away") == 0) { + result = DIRECTION_ADJUST_AWAY; + } else { + result = getValidInt(value, filename, command); + } + + return result; +} + +// Caskey, Damon V. +// 2018-10-13 +// +// Return an adjusted entity direction based +// on original direction, target direction +// and direction adjust setting. +e_direction direction_get_adjustment_result(entity* acting_entity, const entity* target_entity, const e_direction_adjust adjustment) +{ + if (!acting_entity || !target_entity) + { + borShutdown(1, "direction_get_adjustment_result() was passed a NULL pointer."); + } + + const e_direction acting_direction = acting_entity->direction; + const e_direction target_direction = target_entity->direction; + + const float acting_position_x = acting_entity->position.x; + const float target_position_x = target_entity->position.x; + + // Apply direction adjustment. + switch (adjustment) + { + default: + case DIRECTION_ADJUST_NONE: + + // Use original direction. + return acting_direction; + break; + + case DIRECTION_ADJUST_SAME: + + // Same as target direction. + return target_direction; + break; + + case DIRECTION_ADJUST_OPPOSITE: + + // Opposite of target direction. + if (target_direction == DIRECTION_LEFT) + { + return DIRECTION_RIGHT; + } + else + { + return DIRECTION_LEFT; + } + + break; + + case DIRECTION_ADJUST_RIGHT: + + return DIRECTION_RIGHT; + break; + + case DIRECTION_ADJUST_LEFT: + + return DIRECTION_LEFT; + break; + + case DIRECTION_ADJUST_TOWARD: + + if (acting_position_x < target_position_x) + { + return DIRECTION_RIGHT; + } + else if (acting_position_x > target_position_x) + { + return DIRECTION_LEFT; + } + else + { + return acting_direction; + } + + break; + + case DIRECTION_ADJUST_AWAY: + + if (acting_position_x < target_position_x) + { + return DIRECTION_LEFT; + } + else if (acting_position_x > target_position_x) + { + return DIRECTION_RIGHT; + } + else + { + return acting_direction; + } + + break; + } +} + +/* +* Caskey, Damon V. +* 2018-09-08 +* +* Return true if the target entity has a valid +* bind target and match for the override argument. +*/ +bool check_bind_override(entity *ent, bind_config_t bind_config) { + + if(ent->binding.target) { + if(ent->binding.config & bind_config) { + return TRUE; + } + } + + return FALSE; +} + +void check_move(entity *e) +{ + float x, z; + entity *plat, *tempself; + + if((e->update_mark & UPDATE_MARK_CHECK_MOVE)) + { + return; + } + tempself = self; + self = e; + + x = self->position.x; + z = self->position.z; + + // check moving platform + if((plat = self->landed_on_platform) ) // on the platform? + { + //update platform first to get actual movex and movez + if(!(plat->update_mark & UPDATE_MARK_CHECK_MOVE)) + { + check_move(plat); + } + if(plat->movex || plat->movez) + { + if(self->trymove) + { + if(self->grabbing) + { + check_link_move(plat->movex, plat->movez); + } + else if(!self->link) + { + self->trymove(plat->movex, plat->movez); + } + } + else + { + self->position.x += plat->movex; + self->position.z += plat->movez; + } + } + } + if(!is_frozen(self) ) + { + if(self->nextmove <= _time && (self->movex || self->movez) ) + { + if(self->trymove) + { + // only do linked move while grabwalking for now, + // otherwise some grab moves that use move/movez command may act strangely + if(self->grabbing && self->grabwalking) + { + check_link_move(self->movex, self->movez); + } + else// if(!self->link || self->grabbing) + { + if(1 != self->trymove(self->movex, self->movez) && self->idling) + { + self->pathblocked += _time % 2; + } + else + { + self->pathblocked = 0; + } + } + } + else + { + self->position.x += self->movex; + self->position.z += self->movez; + } + } + if(self->nextmove <= _time) + { + self->nextmove = _time + 1; + } + + } + self->movex = self->position.x - x; + self->movez = self->position.z - z; + self->update_mark |= UPDATE_MARK_CHECK_MOVE; + self = tempself; +} + +void ent_post_update(entity *e) { + + check_gravity(e);// check gravity + //check_entity_collision_for(e); + check_move(e); +} + +// arrange the list and reduce its length +void arrange_ents() { + + int i, ind = -1; + entity *temp; + + if(ent_count == 0) { + return; + } + + /* + * Finish movement for every entity before + * resolving binds. Movement may change a + * bind target's animation, such as entering + * ANI_HITWALL, and all bound entities must + * observe the completed target state regardless + * of their order in ent_list. + */ + for(i = 0; i < ent_max; i++) { + if(ent_list[i]->exists) { + ent_post_update(ent_list[i]); + } + } + + /* + * Resolve binding after all target movement + * and animation changes. + */ + for(i = 0; i < ent_max; i++) { + if(ent_list[i]->exists) { + adjust_bind(ent_list[i]); + } + } + + if(ent_max != ent_count) { + + for(i = 0; i < ent_max; i++) { + + if(!ent_list[i]->exists && ind < 0) { + + ind = i; + + } else if(ent_list[i]->exists && ind >= 0) { + temp = ent_list[i]; + ent_list[i] = ent_list[ind]; + ent_list[ind] = temp; + ind++; + } + } + ent_max = ent_count; + } + + for(i = 0; i < ent_max; i++) { + ent_list[i]->movex = ent_list[i]->movez = 0; + } +} + +// Update all entities that wish to think or animate in this cycle. +// All loops are separated because "self" might die during a pass. +void update_ents() +{ + int i; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && _time != ent_list[i]->timestamp)// dont update fresh entity + { + self = ent_list[i]; + self->update_mark = UPDATE_MARK_NONE; + if(level) + { + check_lost(); // check lost caused by level scrolling or lifespan + } + if(!self->exists) + { + continue; + } + // expand _time incase being frozen + if(is_frozen(self)) + { + expand_time(self); + } + else + { + + execute_updateentity_script(self);// execute a script + if(!self->exists) + { + continue; + } + check_ai();// check ai + if(!self->exists) + { + continue; + } + update_animation(); // if not frozen, update animation + if(!self->exists) + { + continue; + } + check_attack();// Collission detection + if(!self->exists) + { + continue; + } + update_health();// Update displayed health + self->movex += self->velocity.x * self->speedmul * (100.0 / global_config.game_speed); + self->movez += self->velocity.z * self->speedmul * (100.0 / global_config.game_speed); + } + } + }//end of for + arrange_ents(); + /* + if(time>=nextplan){ + plan(); + nextplan = time+global_config.game_speed/2; + }*/ +} + + +void display_ents() +{ + unsigned f; + int i, z, wall = 0, wall2; + entity *e = NULL; + entity *other = NULL; + int qx; + int qy; + int sy; + int sz; + int alty; + int in_air; + int sortid; + float basemap; + float temp1, temp2; + int useshadow = 0; + int can_mirror = 0; + int shadowz; + s_drawmethod *drawmethod = NULL; + s_drawmethod commonmethod; + s_drawmethod shadowmethod; + int use_mirror = (level && level->mirror); + + int o_scrx = screenx, o_scry = screeny, scrx, scry; + + if(level) + { + shadowz = SHADOW_Z; + } + else + { + shadowz = MIN_INT + 100; + } + + for(i = 0; i < ent_max; i++) + { + if(ent_list[i] && ent_list[i]->exists) + { + e = ent_list[i]; + // in a loop need to initialize to reset prev val. to avoid bugs. + z = basemap = qx = qy = sy = sz = alty = sortid = temp1 = temp2 = 0; + + if(Script_IsInitialized(e->scripts->ondraw_script)) + { + ent_stack[ent_stack_size++] = e; + } + //if(!e->exists) continue; // just in case kill is called in the script + + /* + * If we have status, draw to screen (ex. boss life). + */ + + if(e->modeldata.hud_popup) + { + drawenemystatus(e); + } + + sortid = e->sortid; + scrx = o_scrx - ((e->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_x_offset); + scry = o_scry - ((e->modeldata.quake_config & QUAKE_CONFIG_DISABLE_SELF) ? 0 : gfx_y_offset); + + if(freezeall || !(e->blink && (_time % (global_config.game_speed / 10)) < (global_config.game_speed / 20))) + { + float eheight = T_WALKOFF, eplatheight = 0; + + // get the height of the entity + if ( e->animation->platform ) + { + s_anim *anim = e->animation; + + if (anim->platform[e->animpos][PLATFORM_HEIGHT]) + { + eplatheight += anim->platform[e->animpos][PLATFORM_HEIGHT]; + } + + } + if ( e->modeldata.size.y && eplatheight <= 0 ) eheight += e->modeldata.size.y; + else eheight += eplatheight; + + // If special is being executed, display all entities regardless + f = e->animation->sprite[e->animpos]; + + //other = check_platform(e->position.x, e->position.z, e); + other = check_platform_below(e->position.x, e->position.z, e->position.y+eheight, e); + wall = checkwall_index(e->position.x, e->position.z); + + if (e->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_BASEMAP) + { + basemap = check_basemap(e->position.x, e->position.z); + } + + if(f < sprites_loaded) + { + // var "z" takes into account whether it has a setlayer set, whether there are other entities on + // the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and + // whether the entity is grabbing someone and has grabback set + + z = (int)e->position.z; // Set the layer offset + + if(other && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !other->modeldata.setlayer) + { + float zdepth = (float)( (float)e->position.z - (float)other->position.z + + (float)other->animation->platform[other->animpos][PLATFORM_DEPTH] - + (float)other->animation->platform[other->animpos][PLATFORM_Z] ); + + if( + e->link // Linked to entity. + && ((e->modeldata.grabback && !e->grabbing) // Have grab back AND not grabbing. + || (e->link->modeldata.grabback && e->link->grabbing) // Linked has grab back and is grabbing. + || e->grabbing) // Grabbing. + ) + { + + // Make sure entities get displayed in front of obstacle and grabbee + sortid = other->sortid + zdepth + 2; + e->sortid = sortid; + z = (int)( other->position.z + 2 ); + } + + else + { + //if ( e->model->type == TYPE_PLAYER ) debug_printf("zdepth: %f",zdepth); + + // Entity should always display in front of the obstacle + sortid = other->sortid + zdepth + 1; + e->sortid = sortid; + z = (int)( other->position.z + 1 ); + } + + } + + // In most cases we want any spawned entity to + // default in front of owner. + if(e->owner) + { + // If this entity is not an exception to the rule, + // move its display order in front of owner. + if (!(self->modeldata.aimove & AIMOVE1_STAR)) + { + sortid = e->owner->sortid + 1; + } + } + + if(e->modeldata.setlayer) + { + z = HOLE_Z + e->modeldata.setlayer; // Setlayer takes precedence + } + + drawmethod = e->animation->drawmethods ? getDrawMethod(e->animation, e->animpos) : NULL; + + if(e->drawmethod->config & DRAWMETHOD_CONFIG_ENABLED) + { + drawmethod = (e->drawmethod); + } + if(!drawmethod) + { + commonmethod = plainmethod; + } + else + { + commonmethod = *drawmethod; + } + drawmethod = &commonmethod; + + if(e->modeldata.alpha >= 1 && e->modeldata.alpha <= MAX_BLENDINGS) + { + if(drawmethod->alpha == BLEND_MODE_MODEL) + { + drawmethod->alpha = e->modeldata.alpha; + } + } + + // Color selection. If drawmethod does not yet have a color table pointer, then + // we need to find one here. + if(!drawmethod->table) + { + // Drawmethod remap by index. Is there a value? + if(drawmethod->remap >= 1) + { + // Does the value fall within range of tables loaded? If so, use + // value to locate the color table by index, and then populate + // drawmethod table value with the color table pointer. + if (drawmethod->remap <= e->modeldata.maps_loaded) + { + drawmethod->table = model_get_colourmap(&(e->modeldata), drawmethod->remap); + } + } + + // Color selection by entity property. Does it have a value? Note that script functions + // and most text values in OpenBOR give the appearance this property is an integer index. + // In actuality those functions accept an index, but immediately use it to locate a color + // table pointer to populate this property. See ent_set_colourmap. + if(e->colourmap) + { + // We don't want to override drawmethods, so first check drawmethod + // remap to make sure it is disabled (0 = force default, 1+ force alternates). + // If it is (dsiabled) then use property value to populate drawmethod table. + if(drawmethod->remap < 0) + { + drawmethod->table = e->colourmap; + } + } + + // If we haven't populated the drawmethod table yet, use + // model's default color table. + if(!drawmethod->table) + { + drawmethod->table = e->modeldata.palette; + } + + // If globalmap is true, then author wants entity to use the current + // global color table. + if(e->modeldata.globalmap) + { + // If we are in a level and the level has a palette index specified, + // then use that index to find the color table pointer and populate + // drawmethod table. Otherwise, just get the global color table pointer. + if(level && current_palette) + { + drawmethod->table = level->palettes[current_palette - 1]; + } + else + { + drawmethod->table = pal; + } + } + } + + // If we have a dying remap, let's check for the dying flash effect. + if(e->dying) + { + // This checks against both dying percentage thresholds and their associated + // timing. If any pass, then we can move on and apply a flash. + if((e->energy_state.health_current <= e->per1 && e->energy_state.health_current > e->per2 && (_time % (global_config.game_speed / 5)) < (global_config.game_speed / 10)) || + (e->energy_state.health_current <= e->per2 && (_time % (global_config.game_speed / 10)) < (global_config.game_speed / 20))) + { + // Have any HP left? + if(e->energy_state.health_current > 0 ) + { + // Do we have a second dying map? If not, we just use map 1. + if(e->dying2 > 0) + { + // If health is between percentage 1 and 2, use map 1. Otherwise + // use map 2. + if(e->energy_state.health_current <= e->per1 && e->energy_state.health_current > e->per2) + { + drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying); + } + else if(e->energy_state.health_current <= e->per2) + { + drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying2); + } + } + else + { + drawmethod->table = model_get_colourmap(&(e->modeldata), e->dying); + } + } + } + } + + // Draw the entity according to its facing. + if(e->direction == DIRECTION_LEFT) + { + // Reverse the drawmethod flipx. + drawmethod->config ^= DRAWMETHOD_CONFIG_FLIP_X; + + // If the flip rotate is enabled, reverse the + // rotation setting. + if(drawmethod->config & DRAWMETHOD_CONFIG_FLIP_ROTATE && drawmethod->rotate) + { + drawmethod->rotate = 360 - drawmethod->rotate; + } + } + + // don't display if behind the mirror + if(!use_mirror || z > MIRROR_Z) + { + //just a simple check, doesn't work with mirror nor gfxshadow + if(drawmethod->clipw) + { + drawmethod->clipx += (int)(e->position.x - scrx); + drawmethod->clipy += (int)(e->position.z - e->position.y - scry); + } + spriteq_add_sprite((int)(e->position.x - scrx), (int)(e->position.z - e->position.y - scry), z, f, drawmethod, sortid); + } + + can_mirror = (use_mirror && self->position.z > MIRROR_Z); + if(can_mirror) + { + spriteq_add_sprite((int)(e->position.x - scrx), (int)((2 * MIRROR_Z - e->position.z) - e->position.y - scry), 2 * PANEL_Z - z , f, drawmethod, ent_list_size * 100 - sortid); + } + }//end of if(fshadow_config_flags & SHADOW_CONFIG_DISABLED)) + { + if (e->shadow_config_flags & (SHADOW_CONFIG_GRAPHIC_REPLICA_AIR | SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND) && f < sprites_loaded) //gfx shadow + { + useshadow = (e->animation->shadow ? e->animation->shadow[e->animpos] : 1) && shadowcolor && light.y; + //printf("\n %d, %d, %d\n", shadowcolor, light.x, light.y); + + if (useshadow && e->position.y >= 0) + { + in_air = inair(e); + + if ((e->shadow_config_flags & SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND && !in_air) || (e->shadow_config_flags & SHADOW_CONFIG_GRAPHIC_REPLICA_AIR && in_air)) + { + wall = checkwall_below(e->position.x, e->position.z, e->position.y); + if (wall < 0) + { + alty = (int)e->position.y; + temp1 = -1 * e->position.y * light.x / 256; // xshift + temp2 = (float)(-alty * light.y / 256); // zshift + qx = (int)(e->position.x - scrx/* + temp1*/); + qy = (int)(e->position.z - scry/* + temp2*/); + } + else + { + alty = (int)(e->position.y - level->walls[wall].height); + temp1 = -1 * (e->position.y - level->walls[wall].height) * light.x / 256; // xshift + temp2 = (float)(-alty * light.y / 256); // zshift + qx = (int)(e->position.x - scrx/* + temp1*/); + qy = (int)(e->position.z - scry /*+ temp2*/ - level->walls[wall].height); + } + + wall2 = checkwall_below(e->position.x + temp1, e->position.z + temp2, e->position.y); // check if the shadow drop into a hole or fall on another wall + + if (other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !(e->shadow_config_flags & SHADOW_CONFIG_BASE_STATIC)) + { + alty = (int)(e->position.y - (other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT])); + temp1 = -1 * (e->position.y - (other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT])) * light.x / 256; // xshift + temp2 = -e->position.y * light.y / 256; + + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - scry - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT]); // + (other->animation->platform[other->animpos][PLATFORM_DEPTH]/2) + //qy = (int)( e->position.z - e->position.y - scry + (e->position.y-e->base) ); + } + + if (basemap > 0 && !(e->shadow_config_flags & SHADOW_CONFIG_BASE_STATIC)) + { + alty = (int)(e->position.y - basemap); + temp1 = -1 * (e->position.y - basemap) * light.x / 256; // xshift + temp2 = (float)(-alty * light.y / 256); // zshift + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - scry - basemap); + } + + //TODO check platforms, don't want to go through the entity list again right now // && !other after wall2 + if (!(checkhole(e->position.x + temp1, e->position.z + temp2) && wall2 < 0 && !other)) //&& !(wall>=0 && level->walls[wall].height>e->position.y)) + { + if (wall >= 0 && wall2 >= 0) + { + alty += (int)(level->walls[wall].height - level->walls[wall2].height); + /*qx += -1*(level->walls[wall].height-level->walls[wall2].height)*light.x/256; + qy += (level->walls[wall].height-level->walls[wall2].height) - (level->walls[wall].height-level->walls[wall2].height)*light.y/256;*/ + } + else if (wall >= 0) + { + alty += (int)(level->walls[wall].height); + /*qx += -1*level->walls[wall].height*light.x/256; + qy += level->walls[wall].height - level->walls[wall].height*light.y/256;*/ + } + else if (wall2 >= 0) + { + alty -= (int)(level->walls[wall2].height); + /*qx -= -1*level->walls[wall2].height*light.x/256; + qy -= level->walls[wall2].height - level->walls[wall2].height*light.y/256;*/ + } + + /*if (other) + { + alty += (int)(other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]); + }*/ + + // set 2D-LIKE shadow + if ((e->shadow_config_flags & SHADOW_CONFIG_BASE_PLATFORM)) alty = temp1 = temp2 = 0; + + sy = (2 * MIRROR_Z - qy) - 2 * scry; + + if (other && !(e->shadow_config_flags & SHADOW_CONFIG_BASE_STATIC)) z = other->position.z + 1; + else z = shadowz; + + sz = PANEL_Z - HUD_Z; + + if (e->animation->shadow_coords) + { + if (e->direction == DIRECTION_RIGHT) + { + qx += e->animation->shadow_coords[e->animpos][0]; + } + else + { + qx -= e->animation->shadow_coords[e->animpos][0]; + } + qy += e->animation->shadow_coords[e->animpos][1]; + sy -= e->animation->shadow_coords[e->animpos][1]; + } + shadowmethod = plainmethod; + shadowmethod.fillcolor = (shadowcolor > 0 ? shadowcolor : 0); + shadowmethod.alpha = shadowalpha; + shadowmethod.channelb = shadowmethod.channelg = shadowmethod.channelr = shadowopacity; + shadowmethod.table = drawmethod->table; + shadowmethod.scalex = drawmethod->scalex; + shadowmethod.scaley = light.y * drawmethod->scaley / 256; + shadowmethod.config = (shadowmethod.config & ~DRAWMETHOD_CONFIG_FLIP_X) | (drawmethod->config & DRAWMETHOD_CONFIG_FLIP_X); + shadowmethod.config = (shadowmethod.config & ~DRAWMETHOD_CONFIG_FLIP_Y) | (drawmethod->config & DRAWMETHOD_CONFIG_FLIP_Y); + shadowmethod.centery += alty; + + if (shadowmethod.config & DRAWMETHOD_CONFIG_FLIP_Y) + { + shadowmethod.centery = -shadowmethod.centery; + } + if (shadowmethod.scaley < 0) + { + shadowmethod.scaley = -shadowmethod.scaley; + shadowmethod.config ^= DRAWMETHOD_CONFIG_FLIP_Y; + } + shadowmethod.rotate = drawmethod->rotate; + shadowmethod.shiftx = drawmethod->shiftx + light.x; + + spriteq_add_sprite(qx, qy, z, f, &shadowmethod, 0); + if (use_mirror) + { + shadowmethod.config ^= DRAWMETHOD_CONFIG_FLIP_Y; + + shadowmethod.centery = -shadowmethod.centery; + spriteq_add_sprite(qx, sy, sz, f, &shadowmethod, 0); + } + } + } + }//end of gfxshadow + } + + /* Plain (sprite) shadow. */ + if (e->shadow_config_flags & (SHADOW_CONFIG_GRAPHIC_STATIC_AIR | SHADOW_CONFIG_GRAPHIC_STATIC_GROUND)) + { + useshadow = e->animation->shadow ? e->animation->shadow[e->animpos] : e->modeldata.shadow; + if (useshadow < 0) + { + useshadow = e->modeldata.shadow; + } + if (useshadow && e->position.y >= 0 && !(checkhole(e->position.x, e->position.z) && checkwall_below(e->position.x, e->position.z, e->position.y) < 0)) + { + in_air = inair(e); + + if ((e->shadow_config_flags & SHADOW_CONFIG_GRAPHIC_STATIC_GROUND && !in_air) || (e->shadow_config_flags & SHADOW_CONFIG_GRAPHIC_STATIC_AIR && in_air)) + { + + if (other && other != e && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !(e->shadow_config_flags & SHADOW_CONFIG_BASE_STATIC)) + { + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT] - scry); + sy = (int)((2 * MIRROR_Z - e->position.z) - other->position.y - other->animation->platform[other->animpos][PLATFORM_HEIGHT] - scry); + + z = (int)(other->position.z + 1); + sz = 2 * PANEL_Z - z; + } + else if (level && wall >= 0)// && e->position.y >= level->walls[wall].height) + { + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - level->walls[wall].height - scry); + sy = (int)((2 * MIRROR_Z - e->position.z) - level->walls[wall].height - scry); + z = shadowz; + sz = PANEL_Z - HUD_Z; + } + else if (level && basemap > 0 && !(e->shadow_config_flags & SHADOW_CONFIG_BASE_STATIC)) + { + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - basemap - scry); + sy = (int)((2 * MIRROR_Z - e->position.z) - basemap - scry); + z = shadowz; + sz = PANEL_Z - HUD_Z; + } + else + { + qx = (int)(e->position.x - scrx); + qy = (int)(e->position.z - scry); + sy = (int)((2 * MIRROR_Z - e->position.z) - scry); + z = shadowz; + sz = PANEL_Z - HUD_Z; + } + if (e->animation->shadow_coords) + { + if (e->direction == DIRECTION_RIGHT) + { + qx += e->animation->shadow_coords[e->animpos][0]; + } + else + { + qx -= e->animation->shadow_coords[e->animpos][0]; + } + qy += e->animation->shadow_coords[e->animpos][1]; + sy -= e->animation->shadow_coords[e->animpos][1]; + } + + shadowmethod = plainmethod; + shadowmethod.alpha = BLEND_MULTIPLY + 1; + shadowmethod.config = e->direction == DIRECTION_RIGHT ? (shadowmethod.config | DRAWMETHOD_CONFIG_FLIP_X) : (shadowmethod.config & ~DRAWMETHOD_CONFIG_FLIP_X); + + spriteq_add_sprite(qx, qy, z, shadowsprites[useshadow - 1], &shadowmethod, 0); + if (use_mirror) + { + spriteq_add_sprite(qx, sy, sz, shadowsprites[useshadow - 1], &shadowmethod, 0); + } + } + }//end of plan shadow + } + } + + }// end of blink checking + + if(e->arrowon) // Display the players image while invincible to indicate player number + { + if(e->modeldata.player_arrow[(int)e->playerindex].sprite && e->invincible & INVINCIBLE_INTANGIBLE) + { + spriteq_add_sprite((int)(e->position.x - scrx + e->modeldata.player_arrow[(int)e->playerindex].position.x), (int)(e->position.z - e->position.y - scry + e->modeldata.player_arrow[(int)e->playerindex].position.y), (int)e->position.z, e->modeldata.player_arrow[(int)e->playerindex].sprite, NULL, sortid * 2); + } + } + }// end of if(ent_list[i]->exists) + + // reset vars for next loop + + }// end of for + + //defer ondraw script so it can manage spriteq better + for(i = 0; i < ent_stack_size; i++) + { + execute_ondraw_script(ent_stack[i]); + } + ent_stack_size = 0; +} + + + +void toss(entity *ent, float lift) +{ + if(!lift) + { + return; //zero? + } + ent->toss_time = _time + 1; + ent->velocity.y = lift; + ent->position.y += 0.5; // Get some altitude (needed for checks) +} + +entity *findent(int types) +{ + int i; + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists // Must exist. + && (ent_list[i]->modeldata.type & types) // Be a type we are looking for. + && !((ent_list[i]->death_state & (DEATH_STATE_DEAD | DEATH_STATE_CORPSE)) == (DEATH_STATE_DEAD | DEATH_STATE_CORPSE))) // Ignore dead corpses. + { + return ent_list[i]; + } + } + return NULL; +} + + + +int count_ents(int types) +{ + int i; + int count = 0; + for(i = 0; i < ent_max; i++) + { + + count += (ent_list[i]->exists && (ent_list[i]->modeldata.type & types) && !(ent_list[i]->death_state & DEATH_STATE_CORPSE)); + } + return count; +} + +int isItem(entity *e) +{ + return e->modeldata.type & TYPE_ITEM; +} + +int isSubtypeTouch(entity *e) +{ + return e->modeldata.subtype == SUBTYPE_TOUCH; +} + +int isSubtypeWeapon(entity *e) +{ + return e->modeldata.subtype == SUBTYPE_WEAPON; +} + +int isSubtypeProjectile(entity *e) +{ + return e->modeldata.subtype == SUBTYPE_PROJECTILE; +} + +//check if an item is usable by the entity +int normal_test_item(entity *ent, entity *item) +{ + return ( + isItem(item) && + (item->modeldata.stealth.hide <= ent->modeldata.stealth.detect) && + diff(item->position.x, ent->position.x) + diff(item->position.z, ent->position.z) < videomodes.hRes / 2 && + item->animation->vulnerable[item->animpos] && !item->blink && + (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && faction_check_can_damage(item, ent, 0))) && + ( + (isSubtypeWeapon(item) && !ent->weapent && ent->modeldata.weapon_properties.weapon_list && + ent->modeldata.weapon_properties.weapon_count >= item->modeldata.weapon_properties.weapon_index && ent->modeldata.weapon_properties.weapon_list[item->modeldata.weapon_properties.weapon_index - 1] >= 0) + || (isSubtypeProjectile(item) && !ent->weapent) + || (item->energy_state.health_current && (ent->energy_state.health_current < ent->modeldata.health) && ! isSubtypeProjectile(item) && ! isSubtypeWeapon(item)) + ) + ); +} + +int test_item(entity *ent, entity *item) +{ + if (!( + isItem(item) && + item->animation->vulnerable[item->animpos] && !item->blink && + (validanim(ent, ANI_GET) || (isSubtypeTouch(item) && faction_check_can_damage(item, ent, 0))) + )) + { + return 0; + } + if(isSubtypeProjectile(item) && ent->weapent) + { + return 0; + } + if(isSubtypeWeapon(item) && + (ent->weapent || !ent->modeldata.weapon_properties.weapon_list || + ent->modeldata.weapon_properties.weapon_count < item->modeldata.weapon_properties.weapon_index || + ent->modeldata.weapon_properties.weapon_list[item->modeldata.weapon_properties.weapon_index - 1] < 0) + ) + { + return 0; + } + return 1; +} + +int player_test_pickable(entity *ent, entity *item) +{ + if(isSubtypeTouch(item)) + { + return 0; + } + + if(isSubtypeWeapon(item) && ent->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + return 0; + } + + if(diff(ent->base , item->position.y) > 0.1) + { + return 0; + } + + return test_item(ent, item); +} + +int player_test_touch(entity *ent, entity *item) +{ + if(!isSubtypeTouch(item)) + { + return 0; + } + + if(isSubtypeWeapon(item) && ent->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + return 0; + } + + if(diff(ent->base , item->position.y) > 1) + { + return 0; + } + return test_item(ent, item); +} + +entity *find_ent_here(entity *exclude, float x, float z, e_entity_type types, int (*test)(entity *, entity *)) +{ + int i; + for(i = 0; i < ent_max; i++) + { + if( ent_list[i]->exists + && ent_list[i] != exclude + && (ent_list[i]->modeldata.type & types) + && diff(ent_list[i]->position.x, x) < (self->modeldata.grabdistance * 0.83333) + && diff(ent_list[i]->position.z, z) < (self->modeldata.grabdistance / 3) + && ent_list[i]->animation->vulnerable[ent_list[i]->animpos] + && (!test || test(exclude, ent_list[i])) + ) + { + return ent_list[i]; + } + } + return NULL; +} + +int set_idle(entity *ent) +{ + ent->idling = IDLING_PREPARED; + ent->attacking = ATTACKING_NONE; + ent->inpain = IN_PAIN_NONE; + ent->rising = RISING_NONE; + ent->ducking = DUCK_NONE; + ent->inbackpain = 0; + ent->falling = 0; + ent->jumping = 0; + ent->blocking = BLOCK_STATE_NONE; + common_idle_anim(ent); + + /* + Kratus (02-2024) Reset the aiflag running when landing after jumping + Works only if the RUN_CONFIG_LAND is enabled but no directional keys are held + If the RUN_CONFIG_LAND is disabled, the common_jump() function will reset it + */ + if (ent->modeldata.run_config_flags & RUN_CONFIG_LAND) + { + if (!((player[ent->playerindex].keys & FLAG_MOVELEFT) || (player[ent->playerindex].keys & FLAG_MOVERIGHT))) + { + ent->running = RUN_STATE_NONE; + } + } + + return 1; +} + +int set_death(entity *iDie, int type, int reset) +{ + int die = 0; + + //iDie->velocity.x = iDie->velocity.z = iDie->velocity.y = 0; // stop the target + if((iDie->blocking & BLOCK_STATE_ACTIVE) && validanim(iDie, ANI_CHIPDEATH)) + { + ent_set_anim(iDie, ANI_CHIPDEATH, reset); + iDie->idling = IDLING_NONE; + iDie->getting = 0; + iDie->jumping = 0; + iDie->charging = 0; + iDie->attacking = ATTACKING_NONE; + iDie->blocking = BLOCK_STATE_NONE; + iDie->inpain = IN_PAIN_NONE; + iDie->falling = 0; + iDie->rising = RISING_NONE; + iDie->ducking = DUCK_NONE; + return 1; + } + + if(type < 0 || type >= max_attack_types) + { + type = 0; + } + + if ( iDie->inbackpain ) die = animbackdies[type]; + else die = animdies[type]; + + if(validanim(iDie, die)) + { + ent_set_anim(iDie, die, reset); + } + else if( iDie->inbackpain && validanim(iDie, animbackdies[0]) ) + { + ent_set_anim(iDie, animbackdies[0], reset); + } + else if(validanim(iDie, animdies[type])) + { + if ( iDie->inbackpain ) reset_backpain(iDie); + iDie->inbackpain = 0; + ent_set_anim(iDie, animdies[type], reset); + } + else if(validanim(iDie, animdies[0])) + { + if ( iDie->inbackpain ) reset_backpain(iDie); + iDie->inbackpain = 0; + ent_set_anim(iDie, animdies[0], reset); + } + else + { + return 0; + } + + iDie->idling = IDLING_NONE; + iDie->getting = 0; + iDie->jumping = 0; + iDie->charging = 0; + iDie->attacking = ATTACKING_NONE; + iDie->blocking = BLOCK_STATE_NONE; + iDie->inpain = IN_PAIN_NONE; + iDie->falling = 0; + iDie->rising = RISING_NONE; + iDie->ducking = DUCK_NONE; + if(iDie->frozen) + { + unfrozen(iDie); + } + return 1; +} + + +int set_fall(entity *ent, entity *other, s_attack *attack, int reset) +{ + int fall = 0; + + if ( ent->inbackpain ) fall = animbackfalls[attack->attack_type]; + else fall = animfalls[attack->attack_type]; + + if(validanim(ent, fall)) + { + ent_set_anim(ent, fall, reset); + } + else if( ent->inbackpain && validanim(ent, animbackfalls[0]) ) + { + ent_set_anim(ent, animbackfalls[0], reset); + } + else if( validanim(ent, animfalls[attack->attack_type]) ) + { + if ( ent->inbackpain ) reset_backpain(ent); + ent->inbackpain = 0; + ent_set_anim(ent, animfalls[attack->attack_type], reset); + } + else if(validanim(ent, animfalls[0])) + { + if ( ent->inbackpain ) reset_backpain(ent); + ent->inbackpain = 0; + ent_set_anim(ent, animfalls[0], reset); + } + else + { + return 0; + } + + ent->drop = 1; + ent->inpain = IN_PAIN_NONE; + ent->rising = RISING_NONE; + ent->idling = IDLING_NONE; + ent->falling = 1; + ent->jumping = 0; + ent->ducking = DUCK_NONE; + ent->getting = 0; + ent->charging = 0; + ent->attacking = ATTACKING_NONE; + ent->blocking = BLOCK_STATE_NONE; + ent->nograb = 1; + ent->running = RUN_STATE_NONE; //Kratus (01-2024) Resets the aiflag running when falling + + if(ent->frozen) + { + unfrozen(ent); + } + execute_onfall_script(ent, other, attack); + + return 1; +} + +int set_rise(entity *iRise, int type, int reset) +{ + int rise = 0; + + if(type < 0 || type >= max_attack_types) + { + type = 0; + } + + if ( iRise->inbackpain ) rise = animbackrises[type]; + else rise = animrises[type]; + + if(validanim(iRise, rise)) + { + ent_set_anim(iRise, rise, reset); + } + else if( iRise->inbackpain && validanim(iRise, animbackrises[0]) ) + { + ent_set_anim(iRise, animbackrises[0], reset); + } + else if( validanim(iRise, animrises[type]) ) + { + if ( iRise->inbackpain ) reset_backpain(iRise); + iRise->inbackpain = 0; + ent_set_anim(iRise, animrises[type], reset); + } + else if(validanim(iRise, animrises[0])) + { + if ( iRise->inbackpain ) reset_backpain(iRise); + iRise->inbackpain = 0; + ent_set_anim(iRise, animrises[0], reset); + } + else + { + return 0; + } + + iRise->takeaction = common_rise; + // Get up again + iRise->drop = 0; + iRise->falling = 0; + iRise->rising |= RISING_RISE; + iRise->rising &= ~RISING_ATTACK; + iRise->projectile = BLAST_NONE; + iRise->nograb = iRise->nograb_default; //iRise->nograb = 0; + iRise->velocity.x = self->velocity.z = self->velocity.y = 0; + iRise->jugglepoints = iRise->modeldata.jugglepoints; //reset jugglepoints + return 1; +} + +int set_riseattack(entity *iRiseattack, int type, int reset) +{ + int riseattack = 0; + + if( (!validanim(iRiseattack, animriseattacks[type]) || + (iRiseattack->inbackpain && !validanim(iRiseattack, animbackriseattacks[type]) && !validanim(iRiseattack, animriseattacks[type]))) && + iRiseattack->modeldata.riseattacktype == 1 ) + { + type = 0; + } + if(iRiseattack->modeldata.riseattacktype == 0 || type < 0 || type >= max_attack_types) + { + type = 0; + } + + if ( iRiseattack->inbackpain ) riseattack = animbackriseattacks[type]; + else riseattack = animriseattacks[type]; + + if(validanim(iRiseattack, riseattack)) + { + ent_set_anim(iRiseattack, riseattack, reset); + } + else if( iRiseattack->inbackpain && validanim(iRiseattack, animbackriseattacks[0]) ) + { + ent_set_anim(iRiseattack, animbackriseattacks[0], reset); + } + else if( validanim(iRiseattack, animriseattacks[type]) ) + { + if ( iRiseattack->inbackpain ) reset_backpain(iRiseattack); + iRiseattack->inbackpain = 0; + ent_set_anim(iRiseattack, animriseattacks[type], reset); + } + else if(validanim(iRiseattack, animriseattacks[0])) + { + if ( iRiseattack->inbackpain ) reset_backpain(iRiseattack); + iRiseattack->inbackpain = 0; + ent_set_anim(iRiseattack, animriseattacks[0], reset); + } + else + { + return 0; + } + + iRiseattack->takeaction = common_attack_proc; + self->staydown.riseattack_stall = 0; //Reset riseattack delay. + set_attacking(iRiseattack); + iRiseattack->inpain = IN_PAIN_NONE; + iRiseattack->falling = 0; + iRiseattack->ducking = DUCK_NONE; + iRiseattack->rising &= ~RISING_RISE; + iRiseattack->rising |= RISING_ATTACK; + iRiseattack->drop = 0; + iRiseattack->nograb = iRiseattack->nograb_default; //iRiseattack->nograb = 0; + iRiseattack->jugglepoints = iRiseattack->modeldata.jugglepoints; //reset jugglepoints + return 1; +} + +/* +* Caskey, Damon V. +* 2018 +* +* Place entity into appropriate block pain animation +* based on attack type and direction of attack. +* +* Returns true if successful, false if no valid block pain +* animation was found. +*/ +bool set_blockpain(entity *acting_entity, attack_type_t attack_type, int reset) { + animation_id_t animation; + + // If attack type is out of bounds we + // just use normal. + if(attack_type >= max_attack_types) { + attack_type = ATK_NORMAL; + } + + // In front or back? + if (acting_entity->inbackpain) { + animation = animbackblkpains[attack_type]; + + } else { + animation = animblkpains[attack_type]; + } + + if(validanim(acting_entity, animation)) { + ent_set_anim(acting_entity, animation, reset); + + } else if( acting_entity->inbackpain + && validanim(acting_entity, animbackblkpains[ATK_NORMAL]) ) { + + ent_set_anim(acting_entity, animbackblkpains[ATK_NORMAL], reset); + + } else if(validanim(acting_entity, animblkpains[attack_type])) { + + if (acting_entity->inbackpain) { + reset_backpain(acting_entity); + } + + acting_entity->inbackpain = 0; + ent_set_anim(acting_entity, animblkpains[attack_type], reset); + + } else if(validanim(acting_entity, animblkpains[ATK_NORMAL])) { + if (acting_entity->inbackpain) { + reset_backpain(acting_entity); + } + + acting_entity->inbackpain = 0; + ent_set_anim(acting_entity, animblkpains[ATK_NORMAL], reset); + } else { + return false; + } + + acting_entity->takeaction = common_block; + set_blocking(acting_entity); + acting_entity->inpain = IN_PAIN_BLOCK; + acting_entity->rising = RISING_NONE; + acting_entity->ducking = DUCK_NONE; + return true; +} + +int reset_backpain(entity *ent) +{ + if (ent->normaldamageflipdir >= 0) + { + if (ent->normaldamageflipdir == DIRECTION_RIGHT) + { + ent->direction = DIRECTION_RIGHT; + } + else + { + ent->direction = DIRECTION_LEFT; + } + if(ent->direction == DIRECTION_RIGHT) ent->velocity.x = -1*fabsf(ent->velocity.x); + else ent->velocity.x = fabsf(ent->velocity.x); + + return 1; + } + + return 0; +} + +int check_backpain(entity* attacker, entity* defender) { + if ( !(defender->modeldata.pain_config_flags & PAIN_CONFIG_BACK_PAIN)) return 0; + if ( defender->inpain & IN_PAIN_HIT) return 0; + if ( defender->falling ) return 0; + if ( defender->death_state & DEATH_STATE_DEAD) return 0; + if ( ((!defender->direction && attacker->position.x > defender->position.x) || (defender->direction && attacker->position.x < defender->position.x)) ) + { + defender->inbackpain = 1; + return 1; + } else if ( defender->inbackpain ) defender->inbackpain = 0; + + return 0; +} + +int set_pain(entity *iPain, int type, int reset) +{ + int pain = 0; + + iPain->velocity.x = iPain->velocity.z = iPain->velocity.y = 0; // stop the target + if(iPain->modeldata.guardpoints > 0 && iPain->guardpoints <= 0) + { + pain = ANI_GUARDBREAK; + iPain->guardpoints = iPain->modeldata.guardpoints; + } + else if(type == -1 || type >= max_attack_types) + { + pain = ANI_GRABBED; + } + else + { + if ( iPain->inbackpain ) pain = animbackpains[type]; + else pain = animpains[type]; + } + + + if(validanim(iPain, pain)) + { + ent_set_anim(iPain, pain, reset); + } + else if( iPain->inbackpain && validanim(iPain, animbackpains[0]) ) + { + ent_set_anim(iPain, animbackpains[0], reset); + } + else if( (type != -1 && type < max_attack_types) && validanim(iPain, animpains[type]) ) + { + if ( iPain->inbackpain ) reset_backpain(iPain); + iPain->inbackpain = 0; + ent_set_anim(iPain, animpains[type], reset); + } + else if(validanim(iPain, animpains[0])) + { + if ( iPain->inbackpain ) reset_backpain(iPain); + iPain->inbackpain = 0; + ent_set_anim(iPain, animpains[0], reset); + } + else if(validanim(iPain, ANI_IDLE)) + { + if ( iPain->inbackpain ) reset_backpain(iPain); + iPain->inbackpain = 0; + ent_set_anim(iPain, ANI_IDLE, reset); + } + else + { + return 0; + } + + iPain->idling = IDLING_NONE; + iPain->falling = 0; + iPain->rising = RISING_NONE; + iPain->ducking = DUCK_NONE; + iPain->projectile = BLAST_NONE; + iPain->drop = 0; + iPain->attacking = ATTACKING_NONE; + iPain->getting = 0; + iPain->charging = 0; + iPain->jumping = 0; + iPain->blocking = BLOCK_STATE_NONE; + iPain->inpain = IN_PAIN_HIT; + if(iPain->frozen) unfrozen(iPain); + + if(pain == ANI_GRABBED) + { + iPain->inpain = IN_PAIN_NONE; + iPain->rising = RISING_NONE; + iPain->ducking = DUCK_NONE; + if ( iPain->inbackpain ) reset_backpain(iPain); + iPain->inbackpain = 0; + } + + execute_onpain_script(iPain, type, reset); + return 1; +} + +/* + * Copy animations from newmodel to model. If newmodel has anim that is not in model, + * then copy that anim (but no frames with alloc_anim()) from newmodel to model + */ +void normalize_anim_models(s_model *model, s_model *newmodel) +{ + int i = 0; + + for(i = 0; i < max_animations; i++) + { + if(!model->animation[i] && newmodel->animation[i]) + { + model->animation[i] = alloc_anim(); + } + } + + return; +} + +//change model, anim_flag 1: reset animation 0: use original animation +void set_model_ex(entity *ent, char *modelname, int index, s_model *newmodel, int anim_flag) +{ + s_model *model = NULL; + entity tempe; + s_defense *dfs = NULL; + s_offense *ofs = NULL; + int i; + int type = ent->modeldata.type; + + model = ent->model; + tempe.exists = 0; + + if(!newmodel) + { + if(index >= 0) + { + newmodel = model_cache[index].model; + } + else + { + newmodel = findmodel(modelname); + } + } + if(!newmodel) + { + borShutdown(1, "Can't set model for entity '%s', model not found.\n", ent->name); + } + if(newmodel == model) + { + return; + } + + if(!(newmodel->model_flag & MODEL_COPY_FLAG_NO_BASIC)) + { + newmodel->move_config_flags = model->move_config_flags; + + if(!newmodel->speed.x) + { + newmodel->speed.x = model->speed.x; + } + if(!newmodel->runspeed) + { + newmodel->runspeed = model->runspeed; + newmodel->runjumpheight = model->runjumpheight; + newmodel->runjumpdist = model->runjumpdist; + newmodel->run_config_flags = model->run_config_flags; + } + if(newmodel->icon.def < 0) + { + newmodel->icon.def = model->icon.def; + } + if(newmodel->icon.pain < 0) + { + newmodel->icon.pain = model->icon.pain; + } + if(newmodel->icon.get < 0) + { + newmodel->icon.get = model->icon.get; + } + if(newmodel->icon.die < 0) + { + newmodel->icon.die = model->icon.die; + } + if(newmodel->shadow < 0) + { + newmodel->shadow = model->shadow; + } + if(newmodel->knife < 0) + { + newmodel->knife = model->knife; + } + if(newmodel->pshotno < 0) + { + newmodel->pshotno = model->pshotno; + } + if(newmodel->bomb < 0) + { + newmodel->bomb = model->bomb; + } + if(newmodel->star < 0) + { + newmodel->star = model->star; + } + if(newmodel->flash.model_hit < 0) + { + newmodel->flash.model_hit = model->flash.model_hit; + } + if(newmodel->flash.model_block < 0) + { + newmodel->flash.model_block = model->flash.model_block; + } + if(newmodel->dust.fall_land < 0) + { + newmodel->dust.fall_land = model->dust.fall_land; + } + if(newmodel->dust.jump_land < 0) + { + newmodel->dust.jump_land = model->dust.jump_land; + } + if(newmodel->diesound < 0) + { + newmodel->diesound = model->diesound; + } + + for(i = 0; i < max_animations; i++) + { + if(!newmodel->animation[i] && model->animation[i] && model->animation[i]->numframes > 0) + { + newmodel->animation[i] = model->animation[i]; + } + } + //normalize_anim_models(model, newmodel); + } + + // copy the weapon list if model flag is not set to use its own weapon list + if (!(newmodel->model_flag & MODEL_COPY_FLAG_NO_WEAPON)) + { + newmodel->weapon_properties.weapon_index = model->weapon_properties.weapon_index; + if (!newmodel->weapon_properties.weapon_list) + { + newmodel->weapon_properties.weapon_list = model->weapon_properties.weapon_list; + newmodel->weapon_properties.weapon_count = model->weapon_properties.weapon_count; + } + } + + //Make a shallow copy of old entity values, not safe but easy. + //Also copy offense and defense because they are more likely be used by weapons, + //other references are left alone for now + if(Script_IsInitialized(newmodel->scripts->onmodelcopy_script)) + { + tempe = *ent; + dfs = malloc(sizeof(*dfs) * max_attack_types); + ofs = malloc(sizeof(*ofs) * max_attack_types); + + memcpy(dfs, ent->defense, sizeof(*dfs)*max_attack_types); + memcpy(ofs, ent->offense, sizeof(*ofs)*max_attack_types); + + tempe.defense = dfs; + tempe.offense = ofs; + } + + ent_set_model(ent, newmodel->name, anim_flag); + + ent->modeldata.type = type; + + if((newmodel->model_flag & MODEL_COPY_FLAG_NO_SCRIPT)) + { + clear_all_scripts(ent->scripts, 0); + } + + copy_all_scripts(newmodel->scripts, ent->scripts, 0); + + memcpy(ent->defense, ent->modeldata.defense, sizeof(*ent->defense)*max_attack_types); + memcpy(ent->offense, ent->modeldata.offense, sizeof(*ent->offense)*max_attack_types); + + ent_set_colourmap(ent, ent->map); + if(Script_IsInitialized(ent->scripts->onmodelcopy_script)) + { + execute_onmodelcopy_script(ent, &tempe); + if(ofs) + { + free(ofs); + } + if(dfs) + { + free(dfs); + } + } +} + +void set_weapon(entity *ent, int wpnum, int anim_flag) // anim_flag added for scripted midair weapon changing +{ + if(!ent) + { + return; + } +//printf("setweapon: %d \n", wpnum); + + if(ent->modeldata.type & TYPE_PLAYER) // save current weapon for player's weaploss WEAPON_LOSS_CONDITION_STAGE + { + if(ent->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_STAGE) + { + player[(int)ent->playerindex].weapnum = wpnum; + } + else + { + player[(int)ent->playerindex].weapnum = level->setweap; + } + } + + if(ent->modeldata.weapon_properties.weapon_list && wpnum > 0 && wpnum <= ent->modeldata.weapon_properties.weapon_count && ent->modeldata.weapon_properties.weapon_list[wpnum - 1]) + { + set_model_ex(ent, NULL, ent->modeldata.weapon_properties.weapon_list[wpnum - 1], NULL, !anim_flag); + } + else + { + set_model_ex(ent, NULL, -1, ent->defaultmodel, 1); + } +} + +////////////////////////////////////////////////////////////////////////// +// common A.I. code for enemies & NPCs +////////////////////////////////////////////////////////////////////////// + + +entity *melee_find_target() +{ + return NULL; +} + +entity *long_find_target() +{ + return NULL; +} + +entity *block_find_target(int anim, int detect_adj) +{ + int i; + int min; + int max; + int detect; + int index = -1; + min = 0; + max = 9999; + float diffx, diffz, diffd, diffo = 0; + entity *attacker; + + detect = detect_adj + self->modeldata.stealth.detect; + + //find the 'nearest' attacking one + for(i = 0; i < ent_max; i++) + { + attacker = ent_list[i]; + + if (attacker && attacker->exists && attacker != self // Can't target self + && (faction_check_can_damage(attacker, self, 0)) // Type is something attacker can damage. + && (anim < 0 || (anim >= 0 && check_range_target_all(self, attacker, anim, 0, 0))) // Valid animation ID and in range. + && !(attacker->death_state & DEATH_STATE_DEAD) // Must be alive. + && attacker->attacking != ATTACKING_NONE // Must be attacking. + && collision_attack_find_no_block_on_frame(attacker->animation, attacker->animpos, 1) != NULL // Valid blockable attack. + && (diffd = (diffx = diff(attacker->position.x, self->position.x)) + (diffz = diff(attacker->position.z, self->position.z))) >= min + && diffd <= max + && (attacker->modeldata.stealth.hide <= detect) // Stealth factor less then perception factor (allows invisibility). + ) + { + if (index < 0 || diffd < diffo) + { + index = i; + diffo = diffd; + + continue; + } + } + } + if( index >= 0) + { + return ent_list[index]; + } + return NULL; +} + +entity *normal_find_target(int anim, int detect_adj) +{ + + /* + normal_find_target + Author unknown + Date unknown + ~Damon Caskey, 2011_07_22: Add support for detect adjustment. + + int anim: Animation find range will be calculated by. Default to current animation if not passed. + int detect_adj: Local detection adjustment. Allows lesser or greater penetration of target's stealth for location. + */ + + int i; + int min; + int max; + int detect; + int index = -1; + float diffx = 0; + float diffz = 0; + float diffd = 0; + float diffo = 0; + + min = 0; + max = 9999; + + detect = detect_adj + self->modeldata.stealth.detect; + + //find the 'nearest' one + for(i = 0; i < ent_max; i++) + { + // Must exist. + if(!ent_list[i]->exists) + { + continue; + } + + // Can't be self. + if(ent_list[i] == self) + { + continue; + } + + /* Must be hostile toward it. */ + if (!faction_check_is_hostile(self, ent_list[i])) + { + continue; + } + + // If anim is defined, then target must be + // in range of animation. + if(anim >= 0) + { + if(!check_range_target_all(self, ent_list[i], anim, 0, 0)) + { + continue; + } + } + + // Can't be dead. + if(ent_list[i]->death_state & DEATH_STATE_DEAD) + { + continue; + } + + // Get X and Z differences between us and target. We then + // add them up to get a total distance. + diffx = diff(ent_list[i]->position.x, self->position.x); + diffz = diff(ent_list[i]->position.z, self->position.z); + diffd = diffx + diffz; + + // Distance must be within min and max. + if(diffd <= min || diffd >= max) + { + continue; + } + + // Stealth must not be greater than perception. + if(ent_list[i]->modeldata.stealth.hide > detect) + { + continue; + } + + + if(index < 0 + || (index >= 0 && (!ent_list[index]->animation->vulnerable[ent_list[index]->animpos] || ent_list[index]->invincible & INVINCIBLE_INTANGIBLE)) + // don't turn to the one on the back + || ((self->position.x < ent_list[i]->position.x) == (self->direction == DIRECTION_RIGHT) && diffd < diffo) + ) + { + index = i; + diffo = diffd; + } + + + } + + if(index >= 0) + { + return ent_list[index]; + } + return NULL; +} + +//Used by default A.I. pattern +// A.I. characters try to find a pickable item +entity *normal_find_item() +{ + + int i; + int index = -1; + entity *ce = NULL; + //find the 'nearest' one + for(i = 0; i < ent_max; i++) + { + ce = ent_list[i]; + + if( ce->exists && normal_test_item(self, ce) ) + { + if(index < 0 || diff(ce->position.x, self->position.x) + diff(ce->position.z, self->position.z) < diff(ent_list[index]->position.x, self->position.x) + diff(ent_list[index]->position.z, self->position.z)) + { + index = i; + } + } + } + if( index >= 0) + { + return ent_list[index]; + } + return NULL; +} + +int long_attack() +{ + return 0; +} + +int melee_attack() +{ + return 0; +} + +// chose next attack in atchain, if succeeded, return 1, otherwise return 0. +int perform_atchain() +{ + int pickanim = 0; + + if(self->modeldata.chainlength <= 0) { + return 0; + } + + /* + * Even if we miss, cotinue the combo chain + * unless time has expired. + */ + if(self->combotime > _time) { + self->combostep[0]++; + } else { + self->combostep[0] = 1; + } + + if(self->modeldata.atchain[self->combostep[0] - 1] == 0) { // 0 means the chain ends + self->combostep[0] = 1; + } + + if(validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) ) { + if(((self->combostep[0] == 1 || !(self->modeldata.combostyle & 1)) && (self->modeldata.type & TYPE_PLAYER)) || // player should use attack 1st step without checking range + + (!(self->modeldata.combostyle & 1) && normal_find_target(animattacks[self->modeldata.atchain[0] - 1], 0)) || // normal chain just checks the first attack in chain(guess no one like it) + + ((self->modeldata.combostyle & 1) && normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0))) { // combostyle 1 checks all anyway. + pickanim = 1; + + } else if((self->modeldata.combostyle & 1) && self->combostep[0] != 1) { // ranged combo? search for a valid attack + + while(++self->combostep[0] <= self->modeldata.chainlength) { + if(self->modeldata.atchain[self->combostep[0] - 1] && + validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1]) && + (self->combostep[0] == self->modeldata.chainlength || + normal_find_target(animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 0))) { + pickanim = 1; + break; + } + } + } + + } else { + self->combostep[0] = 0; + } + + if(pickanim && validanim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1])){ + self->takeaction = common_attack_proc; + set_attacking(self); + ent_set_anim(self, animattacks[self->modeldata.atchain[self->combostep[0] - 1] - 1], 1); + } + + if(!pickanim || self->combostep[0] > self->modeldata.chainlength) { + self->combostep[0] = 0; + } + + if((self->modeldata.combostyle & 2)) { + self->combotime = _time + combodelay; + } + + return pickanim; +} + +void upper_prepare() +{ + int predir = self->direction; + + entity *target = normal_find_target(ANI_UPPER, 0); + + self->velocity.x = self->velocity.z = 0; //stop + + if(!target) + { + self->idling = IDLING_PREPARED; + self->takeaction = NULL; + return; + } + + //check if target is behind, so we can perform a turn back animation + if(!(self->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP)) + { + self->direction = (self->position.x < target->position.x); + } + if(predir != self->direction && validanim(self, ANI_TURN)) + { + self->takeaction = common_turn; + self->direction = predir; + set_turning(self); + ent_set_anim(self, ANI_TURN, 0); + return; + } + + // Wait... + if(_time < self->stalltime) + { + return; + } + + // Target jumping? Try uppercut! + if(target && target->jumping) + { + self->takeaction = common_attack_proc; + set_attacking(self); + self->velocity.z = self->velocity.x = 0; + // Don't waste any time! + ent_set_anim(self, ANI_UPPER, 0); + return; + } +} + +/* +* Add one animation to the temporary AI attack-choice +* table. A capacity failure is fatal because continuing +* would overwrite unrelated memory. +*/ +static void ai_attack_choice_append(int* choice_count, const int animation) +{ + if(!choice_count + || *choice_count < 0 + || !ai_attack_choices + || (size_t)*choice_count >= ai_attack_choice_capacity) { + borShutdown( + 1, + "AI attack-choice table exceeds its capacity of %zu animations.\n", + ai_attack_choice_capacity + ); + + return; + } + + ai_attack_choices[*choice_count] = animation; + (*choice_count)++; +} + +void normal_prepare() +{ + int i, j; + int found = 0, special = 0; + int predir = self->direction; + + entity *target = normal_find_target(-1, 0); + + self->velocity.x = self->velocity.z = 0; //stop + + if(!target) + { + self->idling = IDLING_PREPARED; + self->takeaction = NULL; + return; + } + + //check if target is behind, so we can perform a turn back animation + if(!(self->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP)) + { + self->direction = (self->position.x < target->position.x); + } + if(predir != self->direction && validanim(self, ANI_TURN)) + + { + self->takeaction = common_turn; + self->direction = predir; + set_turning(self); + ent_set_anim(self, ANI_TURN, 0); + return; + } + + // Wait... + if(_time < self->stalltime) + { + return; + } + // let go the projectile, well + if( self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && + validanim(self, ANI_THROWATTACK) && + check_range_target_all(self, target, ANI_THROWATTACK, 0, 0)) + { + self->takeaction = common_attack_proc; + set_attacking(self); + ent_set_anim(self, ANI_THROWATTACK, 0); + return ; + } + + // move freespecial check here + + for(i = 0; i < max_freespecials; i++) + { + if(validanim(self, animspecials[i]) && + (check_energy(ENERGY_TYPE_MP, animspecials[i]) || + check_energy(ENERGY_TYPE_HP, animspecials[i])) && + check_range_target_all(self, target, animspecials[i], 0, 0)) + { + ai_attack_choice_append(&found, animspecials[i]); + } + } + if((rand32() & 7) < 2) + { + if(found && check_costmove(ai_attack_choices[(rand32() & 0xffff) % found], 1, 0) ) + { + return; + } + } + special = found; + + if(self->modeldata.chainlength > 1) // have a chain? + { + if(perform_atchain()) + { + return; + } + } + else if (self->ducking & DUCK_ACTIVE) + { + self->takeaction = common_attack_proc; + set_attacking(self); + ent_set_anim(self, ANI_DUCKATTACK, 0); + return; + } + else // dont have a chain so just select an attack randomly + { + // Pick an attack + for(i = 0; i < max_attacks; i++) + { + if( validanim(self, animattacks[i]) && + check_range_target_all(self, target, animattacks[i], 0, 0)) + { + // a trick to make attack 1 has a greater chance to be chosen + // 6 5 4 3 2 1 1 1 1 1 .... + for(j = ((5 - i) >= 0 ? (5 - i) : 0); j >= 0; j--) + { + ai_attack_choice_append(&found, animattacks[i]); + } + } + } + if(found > special) + { + self->takeaction = common_attack_proc; + set_attacking(self); + ent_set_anim(self, ai_attack_choices[special + (rand32() & 0xffff) % (found - special)], 0); + return; + } + } + + // if no attack was picked, just choose a random one from the valid list + if(special && check_costmove(ai_attack_choices[(rand32() & 0xffff) % special], 1, 0)) + { + return; + } + + // No attack to perform, return to A.I. root + self->idling = IDLING_PREPARED; + self->takeaction = NULL; +} + +void common_jumpland() +{ + if(self->animating) + { + return; + } + self->takeaction = NULL; + set_idle(self); +} + +//A.I characters play the jump animation +void common_jump() +{ + entity *dust; + + if(inair(self)) + { + //printf("%f %f %f %d\n", self->base, self->position.y, self->velocity.y, self->landed_on_platform); + return; + } + + if(self->velocity.y <= 0) // wait if it is still go up + { + self->velocity.y = 0; + self->position.y = self->base; + + self->jumping = 0; + self->ducking = DUCK_NONE; + self->attacking = ATTACKING_NONE; + + if (!(self->modeldata.run_config_flags & RUN_CONFIG_LAND)) + { + self->running = RUN_STATE_NONE; + } + + self->velocity.z = self->velocity.x = 0; + + // check if jumpland animation exists and not using landframe + if(validanim(self, ANI_JUMPLAND) && self->animation->landframe.frame == FRAME_NONE) + { + self->takeaction = common_jumpland; + ent_set_anim(self, ANI_JUMPLAND, 0); + if(self->modeldata.dust.jump_land >= 0) + { + dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL); + if(dust) + { + dust->spawntype = SPAWN_TYPE_DUST_LAND; + dust->base = self->position.y; + dust->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(dust); + } + } + } + else + { + if(self->modeldata.dust.jump_land >= 0 && self->animation->landframe.frame == FRAME_NONE) + { + dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_land, NULL); + if(dust) + { + dust->spawntype = SPAWN_TYPE_DUST_LAND; + dust->base = self->position.y; + dust->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(dust); + } + } + if(self->animation->landframe.frame != FRAME_NONE && self->animating) + { + return; + } + + self->takeaction = NULL; // back to A.I. root + set_idle(self); + } + } +} + +//A.I. characters spawn +void common_spawn() +{ + self->idling = IDLING_NONE; + if(self->animating) + { + return; + } + self->takeaction = NULL; // come to life + set_idle(self); +} + +//A.I. characters drop from the sky +void common_drop() +{ + if(inair(self)) + { + return; + } + self->idling = IDLING_PREPARED; + self->takeaction = NULL; + if(self->energy_state.health_current <= 0) + { + kill_entity(self, KILL_ENTITY_TRIGGER_DROP_NO_HEALTH); + } +} + +//Similar as above, walk off a wall/cliff +void common_walkoff() +{ + if(inair(self) || self->animating) + { + return; + } + self->takeaction = NULL; + set_idle(self); +} + +// play turn animation and then flip +void common_turn() +{ + if(!self->animating) + { + self->takeaction = NULL; + self->velocity.x = self->velocity.z = 0; + self->direction = !self->direction; + set_idle(self); + } +} + +// switch to land animation, land safely +void doland() +{ + self->velocity.x = self->velocity.z = 0; + self->drop = 0; + self->projectile = BLAST_NONE; + self->damage_on_landing.attack_force = 0; + self->damage_on_landing.attack_type = ATK_NONE; + if(validanim(self, ANI_LAND)) + { + self->takeaction = common_land; + self->direction = !self->direction; + ent_set_anim(self, ANI_LAND, 0); + } + else + { + self->takeaction = NULL; + set_idle(self); + } +} + +void common_fall() +{ + // Still falling? + if(self->falling || inair(self) || self->velocity.y) + { + return; + } + + // Landed. Let's see if we could land + // safely. + if(self->projectile != BLAST_NONE) + { + if (self->projectile & BLAST_TOSS) + { + // damage_on_landing.attack_force==-2 means a player has pressed up+jump and has a land animation + if ((autoland == 1 && self->damage_on_landing.attack_force == ATTACK_FORCE_LAND_AUTO) || self->damage_on_landing.attack_force == ATTACK_FORCE_LAND_COMMAND) + { + // Added autoland option for landing + doland(); + return; + } + + self->falling = 0; + } + } + + // Drop Weapon due to Enemy Falling. + //if(self->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_FALL) dropweapon(1); + + if(self->boss && level_completed) + { + tospeedup = 1; + } + + // Pause a bit... + self->takeaction = common_lie; + self->stalltime = _time + MAX(0, (int)(self->staydown.rise + global_config.game_speed - self->modeldata.risetime.rise)); //Set rise delay. + self->staydown.riseattack_stall = _time + MAX(0, (int)(self->staydown.riseattack - self->modeldata.risetime.riseattack)); //Set rise attack delay. + self->staydown.rise = 0; //Reset staydown. + self->staydown.riseattack = 0; //Reset staydown atk. +} + +void common_try_riseattack() +{ + entity *target; + if(!validanim(self, ANI_RISEATTACK)) + { + return; + } + + target = normal_find_target(ANI_RISEATTACK, 0); + if(!target) + { + self->direction = !self->direction; + target = normal_find_target(ANI_RISEATTACK, 0); + self->direction = !self->direction; + } + + if(target) + { + self->direction = (target->position.x > self->position.x); // Stands up and swings in the right direction depending on chosen target + set_riseattack(self, self->last_damage_type, 0); + } +} + +/* +* Caskey, Damon V. +* 2023-03-28 +* +* Run death sequece (if any) for entity. +*/ +int death_try_sequence_damage(entity* acting_entity, e_death_config_flags death_sequence, e_death_sequence_acting_event acting_event) +{ + int result = 0; + attack_type_t attack_type = acting_entity->last_damage_type; + e_death_state death_state = acting_entity->death_state; + + if (death_state & DEATH_STATE_AIR) + { + /* + * Fall first? This works by just turning + * control back over to the damage function + * and letting it handle fall routines. + */ + if ((death_sequence & DEATH_CONFIG_FALL_LAND_AIR && acting_event != DEATH_TRY_SEQUENCE_ACTING_EVENT_LIE) || (death_sequence & DEATH_CONFIG_FALL_LIE_AIR && acting_entity->animating)) + { + /* Turn on blinking? */ + if (death_sequence & DEATH_CONFIG_BLINK_FALL_AIR) + { + acting_entity->blink = 1; + } + + result = 0; + return result; + } + + /* Play death animation? */ + if (death_sequence & DEATH_CONFIG_DEATH_AIR) + { + /* Turn on blinking? */ + if (death_sequence & DEATH_CONFIG_BLINK_DEATH_AIR) + { + acting_entity->blink = 1; + } + + acting_entity->velocity.x = 0; + acting_entity->velocity.y = 0; + acting_entity->velocity.z = 0; + + set_death(acting_entity, attack_type, 0); + + result = 1; + + /* Allow death animation to finish. */ + if (acting_entity->animating) + { + return result; + } + } + + /* + * Remove entity from the screen? + */ + if (death_sequence & DEATH_CONFIG_REMOVE_VANISH_AIR) + { + acting_entity->takeaction = (acting_entity->modeldata.type & TYPE_PLAYER) ? player_blink : suicide; + + /* + * If blink requested, we turn on blink + * effect and set a delay before killing + * self. + */ + if (death_sequence & DEATH_CONFIG_BLINK_REMOVE_AIR) + { + acting_entity->blink = 1; + acting_entity->stalltime = _time + global_config.game_speed * 2; + } + } + else if (death_sequence & DEATH_CONFIG_REMOVE_CORPSE_AIR) + { + /* Turn on blinking? */ + if (death_sequence & DEATH_CONFIG_BLINK_REMOVE_AIR) + { + acting_entity->blink = 1; + } + + /* Set corpse flag and disable AI control. */ + if (acting_entity->modeldata.type & TYPE_PLAYER) + { + acting_entity->takeaction = player_die; + } + else + { + acting_entity->death_state |= DEATH_STATE_CORPSE; + acting_entity->noaicontrol = 1; + } + } + } + else + { + if ((death_sequence & DEATH_CONFIG_FALL_LAND_GROUND && acting_event != DEATH_TRY_SEQUENCE_ACTING_EVENT_LIE) || (death_sequence & DEATH_CONFIG_FALL_LIE_GROUND && acting_entity->animating)) + { + result = 0; + return result; + } + + /* Play death animation? */ + if (death_sequence & DEATH_CONFIG_DEATH_GROUND) + { + /* Turn on blinking? */ + if (death_sequence & DEATH_CONFIG_BLINK_DEATH_GROUND) + { + acting_entity->blink = 1; + } + + acting_entity->velocity.x = 0; + acting_entity->velocity.y = 0; + acting_entity->velocity.z = 0; + + set_death(acting_entity, attack_type, 0); + + result = 1; + + /* Allow death animation to finish. */ + if (acting_entity->animating) + { + return result; + } + } + + /* + * Remove entity from the screen? + */ + if (death_sequence & DEATH_CONFIG_REMOVE_VANISH_GROUND) + { + acting_entity->takeaction = (acting_entity->modeldata.type & TYPE_PLAYER) ? player_blink : suicide; + + /* + * If blink requested, we turn on blink + * effect and set a delay before killing + * self. + */ + if (death_sequence & DEATH_CONFIG_BLINK_REMOVE_GROUND) + { + acting_entity->blink = 1; + acting_entity->stalltime = _time + global_config.game_speed * 2; + } + } + else if (death_sequence & DEATH_CONFIG_REMOVE_CORPSE_GROUND) + { + /* Turn on blinking? */ + if (death_sequence & DEATH_CONFIG_BLINK_REMOVE_GROUND) + { + acting_entity->blink = 1; + } + + /* Set corpse flag and disable AI control. */ + if (acting_entity->modeldata.type & TYPE_PLAYER) + { + acting_entity->takeaction = player_die; + } + else + { + acting_entity->death_state |= DEATH_STATE_CORPSE; + acting_entity->noaicontrol = 1; + } + } + } + + return result; +} + +void common_lie() { + + entity* acting_entity = self; + e_death_config_flags death_config; + + // Died? + if(acting_entity->energy_state.health_current <= 0) { + + const s_defense* defense_object = defense_find_current_object(acting_entity, NULL, acting_entity->last_damage_type); + + death_config = defense_object->death_config_flags; + + if (death_config & DEATH_CONFIG_SOURCE_MODEL) { + death_config = acting_entity->modeldata.death_config_flags; + } + + death_try_sequence_damage(acting_entity, death_config, DEATH_TRY_SEQUENCE_ACTING_EVENT_LIE); + + /* + * Apply KO (death) map if we have one. + */ + if (acting_entity->modeldata.colorsets.ko != COLORSET_INDEX_NONE) { + /* + * Wait for animation to finish unless type is set to + * apply map immediately. + */ + + if (acting_entity->modeldata.colorsets.kotype == KO_COLORSET_CONFIG_INSTANT || !acting_entity->animating) { + acting_entity->colourmap = model_get_colourmap(&(acting_entity->modeldata), acting_entity->modeldata.colorsets.ko); + } + } + + return; + } + + if(_time < acting_entity->stalltime || acting_entity->position.y != acting_entity->base || acting_entity->velocity.y) { + return; + } + + set_rise(acting_entity, acting_entity->last_damage_type, 0); +} + +// rise proc +void common_rise() +{ + if(self->animating) + { + return; + } + self->takeaction = NULL; + self->staydown.riseattack_stall = 0; //Reset riseattack delay. + if(self->modeldata.riseinv) + { + self->blink = self->modeldata.riseinv > 0; + self->invinctime = _time + ABS(self->modeldata.riseinv); + self->invincible |= INVINCIBLE_INTANGIBLE; + } + set_idle(self); +} + +// pain proc +void common_pain() +{ + //self->velocity.x = self->velocity.z = 0; // complained + + if(self->animating || inair(self)) + { + return; + } + + self->inpain = IN_PAIN_HIT; + self->rising = RISING_NONE; + self->ducking = DUCK_NONE; + self->inbackpain = 0; + if(self->link) + { +// set_pain(self, -1, 0); + self->takeaction = common_grabbed; + } + else if((self->blocking & (BLOCK_STATE_ACTIVE | BLOCK_STATE_NATIVE)) + == (BLOCK_STATE_ACTIVE | BLOCK_STATE_NATIVE)) + { + self->inpain = IN_PAIN_BLOCK; + self->takeaction = common_block; + ent_set_anim(self, ANI_BLOCK, 1); + } + else + { + self->takeaction = NULL; + set_idle(self); + } +} + +void doprethrow() +{ + entity *other = self->link; + other->takeaction = common_prethrow; + self->takeaction = common_throw_wait; + self->velocity.x = self->velocity.z = self->velocity.y = other->velocity.x = other->velocity.z = other->velocity.y = 0; + ent_set_anim(self, ANI_THROW, 0); +} + +// 1 grabattack 2 grabforward 3 grabup 4 grabdown 5 grabbackward +// other means grab finisher at once + +// Unknown author (utunnels?). +// +// Retooled by Caskey, Damon V. to use named constants +// for selecting which grab attack. +// 2019-05-31 +// +// Perform a grab attack action depending on request and +// current number already performed for of a given +// grab attack. +void dograbattack(int which) +{ + entity *other = self->link; + self->takeaction = common_grabattack; + self->attacking = ATTACKING_ACTIVE; + other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0; + + // If we requested finish attack, do it now. Otherwise + // we'll look at current combostep for the selected grab + // attack. If we're at the combo limit, then we finish. + // If not, do the requested attack. + if (which == GRAB_ACTION_SELECT_FINISH) + { + do_grab_attack_finish(self, 0); + } + else + { + ++self->combostep[which]; + if (self->combostep[which] < 3 && validanim(self, grab_attacks[which][0])) + { + ent_set_anim(self, grab_attacks[which][0], 0); + } + else + { + do_grab_attack_finish(self, which); + } + } +} + +// Caskey, Damon V. +// 2018-04-11 +// +// Choose appropriate grab finish animation +// or do nothing if we can't find one. Returns +// selected animation. +animation_id_t do_grab_attack_finish(entity *ent, int which) +{ + animation_id_t animation; + + // Clear out the combostep array since this is + // the finishing attack. + memset(ent->combostep, 0, sizeof(*ent->combostep) * 5); + + // Get the finisher animation. + animation = grab_attacks[which][1]; + + // If selected attack animation exists then + // that's what we use. Otherwise default to + // attack3. If THAT fails, we don't do anything. + // The target entity is already unlinked from + // grab before this function was called, so in + // game they are let go with no finishing attack. + if(validanim(ent, animation)) + { + ent_set_anim(ent, animation, 0); + return animation; + } + else if(validanim(ent, ANI_ATTACK3)) + { + // Get the finisher animation. + ent_set_anim(ent, ANI_ATTACK3, 0); + return ANI_ATTACK3; + } + + // Could not find a valid finisher. Return none. + return ANI_NONE; +} + +void common_grab_check() +{ + int rnum, which; + entity *other = self->link; + + if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking)) + { + return; + } + + if(self->base != other->base) + { + // Change this from ->position.y to ->base + self->takeaction = NULL; + ent_unlink(self); + set_idle(self); + return; + } + + if(!nolost && self->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_GRABBING) + { + dropweapon(self, 1); + } + + self->attacking = ATTACKING_NONE; //for checking + + rnum = rand32() & 31; + + if(_time > self->releasetime) + { + if(rnum < 12) + { + // Release + self->takeaction = NULL; + ent_unlink(self); + set_idle(self); + return; + } + else + { + self->releasetime = _time + (global_config.game_speed / 2); + } + } + + if(validanim(self, ANI_THROW) && rnum < 7) + { + if(self->modeldata.throwframewait >= 0) + { + doprethrow(); + } + else + { + dothrow(); + } + return; + } + + // Kratus (04-2023) Implemented vault animation to all A.I. controlled characters + if(validanim(self, ANI_VAULT) && rnum < 6) + { + self->attacking = ATTACKING_ACTIVE; + self->takeaction = common_grabattack; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + ent_set_anim(self, ANI_VAULT, 0); + return; + } + + //grab finisher + if(rnum < 4) + { + dograbattack(GRAB_ACTION_SELECT_FINISH); + return; + } + which = rnum % GRAB_ACTION_SELECT_MAX; + // grab attacks + if(rnum > 12 && validanim(self, grab_attacks[which][0])) + { + dograbattack(which); + return; + } +} + +//grabbing someone +void common_grab() +{ + // if(self->link) return; + if(self->link || (self->modeldata.grabfinish && self->animating && !self->grabwalking)) + { + return; + } + + self->takeaction = NULL; + self->attacking = ATTACKING_NONE; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + set_idle(self); +} + +// being grabbed +void common_grabbed() +{ + // Just check if we're still grabbed... + if(self->link) + { + return; + } + + self->stalltime = 0; + self->takeaction = NULL; + set_idle(self); +} + +// picking up something +void common_get() +{ + if(self->animating) + { + return; + } + + self->getting = 0; + self->takeaction = NULL; + set_idle(self); +} + +// Continue or release block. +void common_block() +{ + // Player type with holdblock, also not in pain + // or has post blockpain holdblock ability. + int player_hold_block_eligible = self->modeldata.block_config_flags & (BLOCK_CONFIG_HOLD_IMPACT | BLOCK_CONFIG_HOLD_INFINITE) + && (self->modeldata.type & TYPE_PLAYER) + && (self->inpain == IN_PAIN_NONE || (self->modeldata.block_config_flags & BLOCK_CONFIG_HOLD_INFINITE)); + + // Controlling player is holding special key. + int player_holding_special = ((player + self->playerindex)->keys & FLAG_SPECIAL); + + // If we are in a block transition, let's see if it is finished. + // If it is, apply block animation. + if (self->animnum == ANI_BLOCKSTART && !self->animating) { + ent_set_anim(self, ANI_BLOCK, 0); + } + + /* + * In "Blockstun", at last frame of animation, and + * have holdblock after blockpain ability? Then we + * return to block. + * + * Otherwise, entity is a player with various other + * flags (see player_hold_block_eligible) but not + * holding special key, or the entity has finihsed + * animation and doesn't match any of the player + * holdblock criteria. It could be another entity + * type, doesn't have holdblock ability, or the + * controlling player isn't holding special key. + * + * In any of those cases, we disable blocking flag + * and return to idle. + */ + + if(self->inpain & IN_PAIN_BLOCK + && (self->modeldata.block_config_flags & BLOCK_CONFIG_HOLD_INFINITE) + && !self->animating + && validanim(self, ANI_BLOCK)) { + + self->inpain = IN_PAIN_NONE; + self->rising = RISING_NONE; + self->inbackpain = 0; + ent_set_anim(self, ANI_BLOCK, 0); + + } else if((player_hold_block_eligible && !player_holding_special) + || (!self->animating && (!player_hold_block_eligible || !player_holding_special))) { + + /* + * Is blockstun complete (no blockpain and + * animation finished)? If yes, play the block + * release animation. If we don't have a block + * release or the block release is finished, + * return to idle. + */ + + if (self->inpain == IN_PAIN_NONE || !self->animating) { + + if (self->animnum == ANI_BLOCKRELEASE && !self->animating) { + self->blocking = BLOCK_STATE_NONE; + self->takeaction = NULL; + set_idle(self); + + } else { + + if (validanim(self, ANI_BLOCKRELEASE)) { + + ent_set_anim(self, ANI_BLOCKRELEASE, 0); + + } else { + + self->blocking = BLOCK_STATE_NONE; + self->takeaction = NULL; + set_idle(self); + } + } + } + } +} + + +void common_charge() +{ + if(self->animating) + { + return; + } + + self->charging = 0; + self->takeaction = NULL; + set_idle(self); +} + +// common code for entities hold an item +entity *drop_item(entity *e) +{ + s_spawn_entry p; + entity *item; + memset(&p, 0, sizeof(p)); + + // Just to make sure there is an item so + // we don't look for data on a NULL pointer. + if(!e->item_properties) + { + return NULL; + } + + p.index = e->item_properties->index; + p.item_properties.index = p.weaponindex = -1; + strcpy(p.alias, e->item_properties->alias); + p.position.y = e->position.y + 0.01; // For check, or an enemy "item" will drop from the sky + p.health[0] = e->item_properties->health; + p.alpha = e->item_properties->alpha; + p.colourmap = e->item_properties->colorset; + p.flip = e->direction; + + item = smartspawn(&p); + + if(item) + { + item->spawntype = SPAWN_TYPE_ITEM; + + item->position.x = e->position.x; + item->position.z = e->position.z; + if(item->position.x < advancex) + { + item->position.x = advancex + 10; + } + else if(item->position.x > advancex + videomodes.hRes) + { + item->position.x = advancex + videomodes.hRes - 10; + } + if(!(level->scrolldir & (SCROLL_UP | SCROLL_DOWN))) + { + if(item->position.z - item->position.y < advancey) + { + item->position.z = advancey + 10; + } + else if(item->position.z - item->position.y > advancey + videomodes.vRes) + { + item->position.z = advancey + videomodes.vRes - 10; + } + } + if(e->boss && (item->modeldata.type & TYPE_ENEMY)) + { + item->boss = 1; + } + } + return item; +} + +//drop the driver, just spawn, dont takedamage +// damage will adjust by the biker +entity *drop_driver(entity *e) +{ + int i; + s_spawn_entry p; + entity *driver; + memset(&p, 0, sizeof(p)); + + if(e->modeldata.rider >= 0) + { + p.index = e->modeldata.rider; + } + else + { + return NULL; // should not happen, just in case + } + p.position.y = e->position.y + 10; + p.weaponindex = -1; + strcpy(p.alias, e->name); + + // Carrying an item, so let's transfer that to spawn + // entry for the driver. + if(e->item_properties) + { + strcpy(p.item_properties.alias, e->item_properties->alias); + + p.item_properties.index = e->item_properties->index; + p.item_properties.colorset = e->item_properties->colorset; + p.item_properties.alpha = e->item_properties->alpha; + p.item_properties.health = e->item_properties->health; + p.item_properties.player_count = e->item_properties->player_count; + } + + /* Match driver color to entity color. */ + p.colourmap = e->map; + + for(i = 0; i < MAX_PLAYERS; i++) + { + p.health[i] = e->modeldata.health; + } + p.boss = e->boss; + + driver = smartspawn(&p); + if(driver) + { + driver->spawntype = SPAWN_TYPE_BIKER; + driver->position.x = e->position.x; + driver->position.z = e->position.z; + } + return driver; +} + + +void checkdeath(entity* target_entity) +{ + if(target_entity->energy_state.health_current > 0) + { + return; + } + target_entity->death_state |= DEATH_STATE_DEAD; + + /* Killed in the air? */ + if (inair(target_entity)) + { + target_entity->death_state |= DEATH_STATE_AIR; + } + + /* In the back? D*** move banner! */ + if (target_entity->inbackpain) + { + target_entity->death_state |= DEATH_STATE_BACK; + } + + //be careful, since the opponent can be other types + if(target_entity->opponent && (target_entity->opponent->modeldata.type & TYPE_PLAYER)) + { + addscore(target_entity->opponent->playerindex, target_entity->modeldata.score); // Add score to the player + } + target_entity->nograb = 1; + target_entity->idling = IDLING_NONE; + target_entity->ducking = DUCK_NONE; + + if(target_entity->modeldata.diesound >= 0) + { + sound_play_sample(target_entity->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100); + } + + // Drop an item if we have one. + if(target_entity->item_properties) + { + if(count_ents(TYPE_PLAYER) > target_entity->item_properties->player_count) + { + drop_item(target_entity); + } + } + + + if(target_entity->boss) + { + target_entity->boss = 0; + --level->bossescount; + if(level->bossescount <= 0 && (target_entity->modeldata.type & TYPE_ENEMY)) + { + kill_all_enemies(); + level_completed = 1; + level_completed_defeating_boss |= 1; + } + } +} + +void checkdamageflip(entity* target_entity, entity *other, s_attack *attack_object, const s_defense* defense_object) +{ + /* Debuging info */ + //attack_dump_object(attack_object); + + target_entity->normaldamageflipdir = DIRECTION_RIGHT; + + int pain_check = 0; + + /* + * Check all the following conditions before we + * take further actions. + */ + + /* + * No damage entity source, or we are the + * damage source. + */ + if (other == NULL || other == target_entity) + { + return; + } + + /* + * Attack did not knock down and + * won't put us into a pain reaction. + */ + if(!target_entity->drop) + { + if (attack_object->no_pain) + { + return; + } + + if (target_entity->modeldata.pain_config_flags & PAIN_CONFIG_PAIN_DISABLE) + { + return; + } + + pain_check = defense_result_pain(attack_object, defense_object); + + if (!pain_check) + { + return; + } + } + + /* We're frozen. */ + if (target_entity->frozen) + { + return; + } + + /* We can't turn. */ + if (target_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP) + { + return; + } + + + /* Apply appropriate direction switch (if any). */ + + //printf("\n\t attack_object->force_direction: %d", attack_object->force_direction); + + switch(attack_object->force_direction) + { + case DIRECTION_ADJUST_TOWARD: + + if (other->position.x < target_entity->position.x) + { + target_entity->direction = DIRECTION_RIGHT; + } + else if (other->position.x > target_entity->position.x) + { + target_entity->direction = DIRECTION_LEFT; + } + else + { + // target_entity->direction = target_entity->direction; + } + + break; + + case DIRECTION_ADJUST_AWAY: + + if (other->position.x < target_entity->position.x) + { + target_entity->direction = DIRECTION_LEFT; + } + else if (other->position.x > target_entity->position.x) + { + target_entity->direction = DIRECTION_RIGHT; + } + else + { + //target_entity->direction = target_entity->direction; + } + + break; + + case DIRECTION_ADJUST_NONE: + + //printf("\n\t DIRECTION_ADJUST_NONE"); + + if(!target_entity->inbackpain ) + { + if(target_entity->position.x < other->position.x) + { + target_entity->direction = DIRECTION_RIGHT; + } + else if(target_entity->position.x > other->position.x) + { + target_entity->direction = DIRECTION_LEFT; + } + } + else + { + if(target_entity->position.x < other->position.x) + { + target_entity->normaldamageflipdir = DIRECTION_RIGHT; + } + else if(target_entity->position.x > other->position.x) + { + target_entity->normaldamageflipdir = DIRECTION_LEFT; + } + } + break; + + case DIRECTION_ADJUST_SAME: + + //printf("\n\t DIRECTION_ADJUST_SAME"); + + target_entity->direction = other->direction; + break; + + case DIRECTION_ADJUST_OPPOSITE: + + //printf("\n\t DIRECTION_ADJUST_OPPOSITE"); + + target_entity->direction = !other->direction; + break; + + case DIRECTION_ADJUST_RIGHT: + + //printf("\n\t DIRECTION_ADJUST_RIGHT"); + + target_entity->direction = DIRECTION_RIGHT; + break; + + case DIRECTION_ADJUST_LEFT: + + //printf("\n\t DIRECTION_ADJUST_LEFT"); + + target_entity->direction = DIRECTION_LEFT; + break; + } + +} + +void checkdamageeffects(entity* target_entity, s_attack* attack) +{ +#define _freeze attack->freeze +#define _maptime attack->maptime +#define _freezetime attack->freezetime +#define _remap attack->forcemap +#define _blast attack->blast +#define _steal attack->steal +#define _seal attack->seal +#define _sealtime attack->sealtime +#define _staydown_rise attack->staydown.rise +#define _staydown_rise_attack attack->staydown.riseattack + + entity *opp = target_entity->opponent; + + // Steal. Take HP from the entity and add it to attacker. + if(_steal && opp && opp != target_entity) + { + // If we have enough HP to withstand the attack, give attacker + // the same amount as attack force. Otherwise just give them + // whatever HP we have left. + if(target_entity->energy_state.health_current >= attack->attack_force) + { + opp->energy_state.health_current += attack->attack_force; + } + else + { + opp->energy_state.health_current += target_entity->energy_state.health_current; + } + + // Cap the effect so attacker doesn't go over their maximum HP. + if(opp->energy_state.health_current > opp->modeldata.health) + { + opp->energy_state.health_current = opp->modeldata.health; + } + } + + // Freeze effect. If this is a freeze attack and we're + // not already frozen, apply a freeze effect and possibly + // remap to freeze palette. If we ARE frozen, then + // unfreeze and knock down instead. + if(_freeze && !target_entity->frozen) + { + + // Set freeze status and expire time. + target_entity->frozen = 1; + if(target_entity->freezetime == 0) + { + target_entity->freezetime = _time + _freezetime; + } + + // 2007-12-14 + // Caskey, Damon V. + // + // If opponents frozen map = -1 or only stun, then don't change the color map. + + if(_remap == -1 && target_entity->modeldata.colorsets.frozen != -1) + { + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), target_entity->modeldata.colorsets.frozen); + } + + target_entity->drop = 0; + } + else if(target_entity->frozen) + { + unfrozen(target_entity); + target_entity->drop = 1; + } + + // If we want to apply a remap without freezing (forcemap attack command) then + // set the map and expire time here. + if(_remap != 0 && !_freeze) + { + /* + * Caskey, Damon V. + * 2020-10-20 + * + * Apply named map if available + * or fall back to direct index. + * + * TODO: Code is pretty messy and + * repetitive. Needs some refinement + * and offloading to a function. + */ + switch (_remap) + { + case MAP_TYPE_BURN: + if (target_entity->modeldata.colorsets.burn != COLORSET_INDEX_NONE) + { + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), target_entity->modeldata.colorsets.burn); + } + break; + case MAP_TYPE_FREEZE: + if (target_entity->modeldata.colorsets.frozen != COLORSET_INDEX_NONE) + { + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), target_entity->modeldata.colorsets.frozen); + } + break; + case MAP_TYPE_KO: + if (target_entity->modeldata.colorsets.ko != COLORSET_INDEX_NONE) + { + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), target_entity->modeldata.colorsets.ko); + } + break; + case MAP_TYPE_SHOCK: + if (target_entity->modeldata.colorsets.shock != COLORSET_INDEX_NONE) + { + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), target_entity->modeldata.colorsets.shock); + } + break; + default: + target_entity->colourmap = model_get_colourmap(&(target_entity->modeldata), _remap); + break; + } + + target_entity->maptime = _time + _maptime; + } + + // Disable specials. Apply seal (Any animation with + // energy_cost > seal) is disabled and time to expire. + if(_seal) + { + target_entity->sealtime = _time + _sealtime; + target_entity->seal = _seal; + } + + // Apply any recursive (damage over time) effects. + recursive_effect_check_apply(target_entity, opp, attack); + + // Static enemies/nodrop enemies cannot be knocked down + if(target_entity->modeldata.pain_config_flags & PAIN_CONFIG_FALL_DISABLE) + { + target_entity->drop = 0; + } + + // Always knock airborne entities down unless we're freezeing them + // or they are specfically immune to in air knockdowns. + if(inair(target_entity) && !target_entity->frozen && !(target_entity->modeldata.pain_config_flags & PAIN_CONFIG_FALL_DISABLE_AIR)) + { + target_entity->drop = 1; + } + + // Immune to hit stun? No knockdown either. + if(attack->no_pain) + { + target_entity->drop = 0; + } + + // If entity will be knocked down, let's apply knockdown specific effects here. + if(target_entity->drop) + { + target_entity->projectile = _blast; + + target_entity->staydown.rise = _staydown_rise; //Staydown: Add to risetime until next rise. + target_entity->staydown.riseattack = _staydown_rise_attack; + } + +#undef _freeze +#undef _maptime +#undef _freezetime +#undef _remap +#undef _blast +#undef _steal +#undef _seal +#undef _sealtime +#undef _staydown_rise +#undef _staydown_rise_attack +} + +/* +* Orginal author and date unknown. +* +* Determines fall status after taking damage. +* +* Caskey, Damon V. +* 2021-09-08 +* +* Slightly reworked to accept the target entity +* as parameter rather than referencing the +* global "self" variable. +* +* Accepts defense object to pass on into +* total damage calculation functions. +*/ +void checkdamagedrop(entity* target_entity, s_attack* attack_object, const s_defense* defense_object) +{ + int attack_drop = attack_object->attack_drop; + float defense_knockdown = defense_object->knockdown; + + /* + * If already falling or attack is a "no reflect", then + * do nothing and exit. + */ + + if (target_entity->drop || attack_object->no_pain) + { + return; + } + + /* + * Legacy behavior. Always fall if in "animal" mode. + */ + + if(target_entity->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + target_entity->drop = 1; + } + + /* Make sure guard break doesn't knock target down. */ + + if(target_entity->modeldata.guardpoints > 0 && target_entity->guardpoints <= 0) + { + attack_drop = 0; + } + + /* + * Reset knockdown count if the knockdowntime + * is expired. + */ + + if(target_entity->knockdowntime && target_entity->knockdowntime < _time) + { + target_entity->knockdowncount = target_entity->modeldata.knockdowncount; + } + + /* + * Defense knockdown works just like damage. It + * is a mutiplier we apply to the attack's drop + * power. We then subjtract the result from target's + * current knockdowncount. + * + * We also update the expire time for resetting + * target's current knockdown. + * + * If target's knockdown count reaches 0, we + * set target's drop status true, and target + * falls in game. + */ + + target_entity->knockdowncount -= (attack_drop * defense_knockdown); + target_entity->knockdowntime = _time + global_config.game_speed; + + if (target_entity->knockdowncount < 0) + { + target_entity->drop = 1; + } +} + +void checkmpadd(entity* target_entity) +{ + entity *other = target_entity->opponent; + if(other == NULL || other == target_entity) + { + return; + } + + if(magic_type == 1 ) + { + other->energy_state.mp_current += other->modeldata.mprate; + + if(other->energy_state.mp_current > other->modeldata.mp) + { + other->energy_state.mp_current = other->modeldata.mp; + } + else if(other->energy_state.mp_current < 0) + { + other->energy_state.mp_current = 0; + } + } +} + +void checkhitscore(entity* target_entity, entity* attacking_entity, s_attack* attack_object) +{ + entity *opp = target_entity->opponent; + if(!opp) + { + return; + } + if(opp != target_entity && (opp->modeldata.type & TYPE_PLAYER)) + { + // Added obstacle so explosions can hurt enemies + addscore(opp->playerindex, attack_object->attack_force * target_entity->modeldata.multiple); // New multiple variable + if (savedata.joyrumble[opp->playerindex]) control_rumble(opp->playerindex, 1, attack_object->attack_force * 2); + } + // Don't animate or fall if hurt by self, since + // it means self fell to the ground already. :) + // Add throw score to the player + else if(attacking_entity == target_entity && target_entity->damage_on_landing.attack_force > 0) + { + addscore(opp->playerindex, attack_object->attack_force); + } +} + +/* +* Caskey, Damon V. +* 2023-04-03 +* +* Accept string input and return +* matching constant. +*/ +e_death_config_flags death_get_config_flag_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_death_config_flags flag; + } flag_lookup_table[] = { + {"none", DEATH_CONFIG_NONE}, + {"default", DEATH_CONFIG_MACRO_DEFAULT}, + {"blink_death_air", DEATH_CONFIG_BLINK_DEATH_AIR}, + {"blink_death_ground", DEATH_CONFIG_BLINK_DEATH_GROUND}, + {"blink_fall_air", DEATH_CONFIG_BLINK_FALL_AIR}, + {"blink_fall_ground", DEATH_CONFIG_BLINK_FALL_GROUND}, + {"blink_remove_air", DEATH_CONFIG_BLINK_REMOVE_AIR}, + {"blink_remove_ground", DEATH_CONFIG_BLINK_REMOVE_GROUND}, + {"death_air", DEATH_CONFIG_DEATH_AIR}, + {"death_ground", DEATH_CONFIG_DEATH_GROUND}, + {"fall_land_air", DEATH_CONFIG_FALL_LAND_AIR}, + {"fall_land_ground", DEATH_CONFIG_FALL_LAND_GROUND}, + {"fall_lie_air", DEATH_CONFIG_FALL_LIE_AIR}, + {"fall_lie_ground", DEATH_CONFIG_FALL_LIE_GROUND}, + {"remove_corpse_air", DEATH_CONFIG_REMOVE_CORPSE_AIR}, + {"remove_corpse_ground", DEATH_CONFIG_REMOVE_CORPSE_GROUND}, + {"remove_vanish_air", DEATH_CONFIG_REMOVE_VANISH_AIR}, + {"remove_vanish_ground", DEATH_CONFIG_REMOVE_VANISH_GROUND}, + {"source_model", DEATH_CONFIG_SOURCE_MODEL}, + }; + + const size_t list_count = sizeof(flag_lookup_table) / sizeof(*flag_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, flag_lookup_table[i].text_name) == 0) + { + return flag_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * none flag. + */ + + printf("\n\n Unknown death config option (%s). \n", value); + return DEATH_CONFIG_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read death configuration arguments directly from the source + line, beginning at the requested item, and combine their flags. +*/ +e_death_config_flags death_get_config_flags_from_command_line( + const char* command_line, + const size_t start_position +) +{ + const char* value; + s_command_argument_reader reader; + e_death_config_flags result = DEATH_CONFIG_NONE; + + command_argument_reader_initialize( + &reader, + command_line, + start_position + ); + + while(command_argument_reader_next(&reader, &value)) { + result |= death_get_config_flag_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-20 +* +* Accept current value and a legacy falldie. +* Returns appropriate config flags to match +* expected legacy behavior. +*/ +e_death_config_flags death_config_get_value_from_falldie(e_death_config_flags current_value, e_falldie_config acting_value) +{ + //printf("\n\n death_config_get_value_from_falldie(%d, %d)", current_value, acting_value); + + e_death_config_flags result = current_value & ~DEATH_CONFIG_MACRO_DEATH_FALL_ALL; + + switch (acting_value) + { + + case FALLDIE_CONFIG_NONE: + result |= DEATH_CONFIG_MACRO_DEFAULT; + break; + + case FALLDIE_CONFIG_DEATH_INSTANT: + result |= DEATH_CONFIG_MACRO_DEATH | DEATH_CONFIG_SOURCE_MODEL; + break; + + case FALLDIE_CONFIG_DEATH_FALL: + result |= DEATH_CONFIG_MACRO_DEATH_FALL_LAND | DEATH_CONFIG_SOURCE_MODEL; + break; + } + + //printf("\n\t result: %d", result); + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-20 +* +* Accept current value. Returns a legacy +* value based on active flags. +*/ +e_falldie_config death_config_get_falldie_from_value(e_death_config_flags acting_value) +{ + //printf("\n\n death_config_get_falldie_from_value(%d)", acting_value); + e_falldie_config result = FALLDIE_CONFIG_NONE; + + if ((acting_value & DEATH_CONFIG_MACRO_DEATH_FALL_LAND) == DEATH_CONFIG_MACRO_DEATH_FALL_LAND) + { + result = FALLDIE_CONFIG_DEATH_FALL; + } + else if ((acting_value & DEATH_CONFIG_MACRO_DEATH) == DEATH_CONFIG_MACRO_DEATH) + { + result = FALLDIE_CONFIG_DEATH_INSTANT; + } + else if ((acting_value & (DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND)) == (DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND)) + { + result = FALLDIE_CONFIG_NONE; + } + + //printf("\n\t result: %d", result); + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-20 +* +* Accept current value and a legacy falldie. +* Returns appropriate config flags to match +* expected legacy behavior. +*/ +e_death_config_flags death_config_get_value_from_nodieblink(e_death_config_flags current_value, e_nodieblink_config acting_value) +{ + //printf("\n\n death_config_get_value_from_nodieblink(%d, %d)", current_value, acting_value); + + e_death_config_flags result = (current_value &= ~(DEATH_CONFIG_MACRO_BLINK | DEATH_CONFIG_MACRO_FALL | DEATH_CONFIG_MACRO_REMOVE)); + + switch (acting_value) + { + + case NODIEBLINK_CONFIG_NONE: + result |= DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND; + break; + + case NODIEBLINK_CONFIG_FALL_LIE_BLINK: + result |= DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND; + break; + + case NODIEBLINK_CONFIG_FALL_LIE_CORPSE: + result |= DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_CORPSE_AIR | DEATH_CONFIG_REMOVE_CORPSE_GROUND; + break; + + case NODIEBLINK_CONFIG_FALL_LIE_VANISH: + result |= DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND; + break; + } + + //printf("\n\t result: %d", result); + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-20 +* +* Accept current value. Returns a legacy +* value based on active flags. +*/ +e_nodieblink_config death_config_get_nodieblink_from_value(e_death_config_flags acting_value) +{ + //printf("\n\n death_config_get_falldie_from_value(%d)", acting_value); + e_nodieblink_config result = NODIEBLINK_CONFIG_NONE; + + if ((acting_value & (DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) == (DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) + { + result = NODIEBLINK_CONFIG_FALL_LIE_VANISH; + } + else if ((acting_value & (DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_CORPSE_AIR | DEATH_CONFIG_REMOVE_CORPSE_GROUND)) == (DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_CORPSE_AIR | DEATH_CONFIG_REMOVE_CORPSE_GROUND)) + { + result = NODIEBLINK_CONFIG_FALL_LIE_CORPSE; + } + else if ((acting_value & (DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) == (DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LIE_AIR | DEATH_CONFIG_FALL_LIE_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) + { + result = NODIEBLINK_CONFIG_FALL_LIE_BLINK; + } + else if ((acting_value & (DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) == (DEATH_CONFIG_BLINK_REMOVE_AIR | DEATH_CONFIG_BLINK_REMOVE_GROUND | DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) + { + result = NODIEBLINK_CONFIG_NONE; + } + + //printf("\n\t result: %d", result); + + return result; +} + +/* +* Caskey, Damon V. +* 2023-04-03 +* +* Accept string input and return +* matching constant. +*/ +e_run_config_flags run_get_config_flag_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_run_config_flags flag; + } flag_lookup_table[] = { + {"none", RUN_CONFIG_NONE}, + {"land", RUN_CONFIG_LAND}, + {"x_left_dash_command", RUN_CONFIG_X_LEFT_DASH_COMMAND}, + {"x_left_dash_fixed", RUN_CONFIG_X_LEFT_DASH_FIXED}, + {"x_left_enabled", RUN_CONFIG_X_LEFT_ENABLED}, + {"x_left_initial", RUN_CONFIG_X_LEFT_INITIAL}, + {"x_right_dash_command", RUN_CONFIG_X_RIGHT_DASH_COMMAND}, + {"x_right_dash_fixed", RUN_CONFIG_X_RIGHT_DASH_FIXED}, + {"x_right_enabled", RUN_CONFIG_X_RIGHT_ENABLED}, + {"x_right_initial", RUN_CONFIG_X_RIGHT_INITIAL}, + {"z_down_dash_command", RUN_CONFIG_Z_DOWN_DASH_COMMAND}, + {"z_down_dash_fixed", RUN_CONFIG_Z_DOWN_DASH_FIXED}, + {"z_down_enabled", RUN_CONFIG_Z_DOWN_ENABLED}, + {"z_down_initial", RUN_CONFIG_Z_DOWN_INITIAL}, + {"z_up_dash_command", RUN_CONFIG_Z_UP_DASH_COMMAND}, + {"z_up_dash_fixed", RUN_CONFIG_Z_UP_DASH_FIXED}, + {"z_up_enabled", RUN_CONFIG_Z_UP_ENABLED}, + {"z_up_initial", RUN_CONFIG_Z_UP_INITIAL}, + }; + + const size_t list_count = sizeof(flag_lookup_table) / sizeof(*flag_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, flag_lookup_table[i].text_name) == 0) + { + return flag_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * none flag. + */ + + printf("\n\n Unknown run config option (%s). \n", value); + return RUN_CONFIG_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read running configuration arguments directly from the source + line, beginning at the requested item, and combine their flags. +*/ +e_run_config_flags run_get_config_flags_from_command_line( + const char* command_line, + const size_t start_position +) +{ + const char* value; + s_command_argument_reader reader; + e_run_config_flags result = RUN_CONFIG_NONE; + + command_argument_reader_initialize( + &reader, + command_line, + start_position + ); + + while(command_argument_reader_next(&reader, &value)) { + result |= run_get_config_flag_from_string(value); + } + + return result; +} + + +/* +* Caskey, Damon V. +* 2023-03-20 +* +* Accept string input and return +* matching constant. +*/ +e_shadow_config_flags shadow_get_config_flag_from_string(const char* value) +{ + const struct + { + const char* text_name; + e_shadow_config_flags flag; + } flag_lookup_table[] = { + {"none", SHADOW_CONFIG_NONE}, + {"default", SHADOW_CONFIG_DEFAULT}, + {"base_platform", SHADOW_CONFIG_BASE_PLATFORM}, + {"base_static", SHADOW_CONFIG_BASE_STATIC}, + {"disabled", SHADOW_CONFIG_DISABLED}, + {"replica_air", SHADOW_CONFIG_GRAPHIC_REPLICA_AIR}, + {"replica_ground", SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND}, + {"static_air", SHADOW_CONFIG_GRAPHIC_STATIC_AIR}, + {"static_ground", SHADOW_CONFIG_GRAPHIC_STATIC_GROUND} + }; + + const size_t list_count = sizeof(flag_lookup_table) / sizeof(*flag_lookup_table); + + for (size_t i = 0; i < list_count; i++) + { + if (stricmp(value, flag_lookup_table[i].text_name) == 0) + { + return flag_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * none flag. + */ + + printf("\n\n Unknown death config option (%s). \n", value); + return SHADOW_CONFIG_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read shadow configuration arguments directly from the source + line and combine their corresponding behavior flags. +*/ +e_shadow_config_flags shadow_get_config_flags_from_command_line( + const char* command_line +) +{ + const char* value; + s_command_argument_reader reader; + e_shadow_config_flags result = SHADOW_CONFIG_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= shadow_get_config_flag_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-19 +* +* Accept current shadow config +* state and a legacy air only +* property. Returns appropriate +* shadow config value. +*/ +e_shadow_config_flags shadow_get_config_from_legacy_aironly(e_shadow_config_flags shadow_config_flags, int legacy_value) +{ + e_shadow_config_flags result = shadow_config_flags; + + if (!legacy_value) + { + /* + * Don't do anything. Text "aironly 0" doesn't + * mean no air shadows, and fortunatly there + * is no legacy aironly script access, so + * we don't need to worry about that. + */ + + return result; + } + + /* + * If either type of ground shadow is turned on, + * then disable it here. + */ + + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND) + { + result &= ~SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND; + result |= SHADOW_CONFIG_GRAPHIC_REPLICA_AIR; + } + + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_STATIC_GROUND) + { + result &= ~SHADOW_CONFIG_GRAPHIC_STATIC_GROUND; + result |= SHADOW_CONFIG_GRAPHIC_STATIC_AIR; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-19 +* +* Accept current shadow config +* state and a legacy gfxshadow +* property. Returns appropriate +* shadow config value. +*/ +e_shadow_config_flags shadow_get_config_from_legacy_gfxshadow(e_shadow_config_flags shadow_config_flags, int legacy_value) +{ + e_shadow_config_flags result = shadow_config_flags; + + /* + * Creator wants to turn off "gfx", so swap any + * existing replica flags for static. + */ + + if (!legacy_value) + { + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_REPLICA_AIR) + { + result |= SHADOW_CONFIG_GRAPHIC_STATIC_AIR; + result &= ~SHADOW_CONFIG_GRAPHIC_REPLICA_AIR; + } + + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND) + { + result |= SHADOW_CONFIG_GRAPHIC_STATIC_GROUND; + result &= ~SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND; + } + + /* + * If no shadow at all set, creator expects + * gfxshadow 0 to set up static shadow. Let's + * initialize the flags here. + */ + + if (!(shadow_config_flags & SHADOW_CONFIG_GRAPHIC_ALL)) + { + result |= (SHADOW_CONFIG_GRAPHIC_STATIC_GROUND | SHADOW_CONFIG_GRAPHIC_STATIC_AIR); + } + + return result; + } + + + /* + * Creator wants to turn on "gfx", so swap any + * existing static flags for replica. + */ + + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_STATIC_GROUND) + { + result &= ~SHADOW_CONFIG_GRAPHIC_STATIC_GROUND; + result |= SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND; + } + + if (shadow_config_flags & SHADOW_CONFIG_GRAPHIC_STATIC_AIR) + { + result &= ~SHADOW_CONFIG_GRAPHIC_STATIC_AIR; + result |= SHADOW_CONFIG_GRAPHIC_REPLICA_AIR; + } + + /* + * If no shadow at all set, creator expects + * gfxshadow to set up replica shadow. Let's + * initialize the flags here. + */ + if (!(shadow_config_flags & SHADOW_CONFIG_GRAPHIC_ALL)) + { + result |= (SHADOW_CONFIG_GRAPHIC_REPLICA_GROUND | SHADOW_CONFIG_GRAPHIC_REPLICA_AIR); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-03-19 +* +* Accept current shadow config +* state and a legacy property. +* Returns appropriate shadow +* config value. +*/ +e_shadow_config_flags shadow_get_config_from_legacy_shadowbase(e_shadow_config_flags shadow_config_flags, e_shadowbase_config legacy_value) +{ + e_shadow_config_flags result = shadow_config_flags; + + /* Remove all existing base flags.*/ + result &= ~SHADOW_CONFIG_BASE_ALL; + + switch (legacy_value) + { + default: + case SHADOWBASE_CONFIG_NONE: + + /* Do nothing. */ + break; + + case SHADOWBASE_CONFIG_NO_CHANGE: + + result |= SHADOW_CONFIG_BASE_STATIC; + break; + + case SHADOWBASE_CONFIG_PLATFORM: + + result |= SHADOW_CONFIG_BASE_PLATFORM; + break; + + case SHADOWBASE_COMBINE: + + result |= (SHADOW_CONFIG_BASE_STATIC | SHADOW_CONFIG_BASE_PLATFORM); + break; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2025-05-21 +* +* Remove trigger parameter reading. As +* of 2025-05-21, this is a bit much, but +* we may want to expand with more options +* later on. +*/ +static const s_remove_config_map remove_config_map[] = { + { "None", REMOVE_CONFIG_NONE }, + { "Hit", REMOVE_CONFIG_HIT }, +}; + +#define REMOVE_CONFIG_MAP_SIZE (sizeof(remove_config_map) / sizeof(remove_config_map[0])) + +e_remove_config get_remove_config_from_string(const char* value) { + if (!value) return REMOVE_CONFIG_NONE; + + // Try to match string (case-insensitive) + for (size_t i = 0; i < REMOVE_CONFIG_MAP_SIZE; i++) { + if (stricmp(value, remove_config_map[i].name) == 0) { + return remove_config_map[i].trigger; + } + } + + // Try to parse as an integer fallback + char* endptr = NULL; + long val = strtol(value, &endptr, 10); + if (endptr != value && *endptr == '\0' && val >= 0) { + return (e_remove_config)val; + } + + // Default fallback + printf("Warning: Unknown Remove value '%s', defaulting to Hit\n", value); + return REMOVE_CONFIG_HIT; +} + +#undef REMOVE_CONFIG_MAP_SIZE + +/* +* Caskey, Damon V. +* 2021-08-30 +* +* Allocate a defense object and return pointer. +*/ +s_defense* defense_allocate_object(void) { + int attack_type; + s_defense* result = NULL; + + /* + * Allocate one extra slot because several legacy paths + * historically expect max_attack_types + 1 storage. + */ + result = calloc(max_attack_types + 1, sizeof(*result)); + + if (!result) { + borShutdown(1, E_OUT_OF_MEMORY); + } + + for (attack_type = 0; attack_type < max_attack_types + 1; attack_type++) { + result[attack_type] = default_defense; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2021-08-30 +* +* Applies value to an attack type element +* of defense. +*/ +void defense_apply_setup_to_property( + char* filename, + char* command, + const char* command_line, + s_defense* defense, + ArgList* arglist, + e_defense_parameters target_parameter +) +{ + //printf("\n\n defense_apply_setup_to_property(%s, %s, %p, %p, %d)", filename, command, defense, arglist, target_parameter); + //printf("\n\t GET_FLOAT_ARGP(2): %f", GET_FLOAT_ARGP(2)); + + /* + * If a NULL pointer gets through, lets + * get out before we cause a NULL pointer + * error + */ + if (!defense) + { + return; + } + + /* + * As of 2021-08-30, the up to date method + * sets one parameter at a time. This is more + * readable in the model text files. It also + * allows easier debug and expansion in the future. + * + * Defense is a bit clunky since we have to send + * a constant to tell us which parameter to update + * but it still beats the legacy method. + */ + + switch (target_parameter) + { + case DEFENSE_PARAMETER_BLOCK_DAMAGE_ADJUST: + defense->block_damage_adjust = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_DAMAGE_MAX: + defense->block_damage_max = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_DAMAGE_MIN: + defense->block_damage_min = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_POWER: + defense->blockpower = GET_FLOAT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_RATIO: + defense->blockratio = GET_FLOAT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_THRESHOLD: + defense->blockthreshold = GET_FLOAT_ARGP(2); + break; + + case DEFENSE_PARAMETER_BLOCK_TYPE: + defense->blocktype = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_DAMAGE_ADJUST: + defense->damage_adjust = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_DAMAGE_MAX: + defense->damage_max = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_DAMAGE_MIN: + defense->damage_min = GET_INT_ARGP(2); + break; + + case DEFENSE_PARAMETER_DEATH_CONFIG: + defense->death_config_flags = death_get_config_flags_from_command_line(command_line, 2); + break; + + case DEFENSE_PARAMETER_FACTOR: + defense->factor = GET_FLOAT_ARGP(2); + break; + + case DEFENSE_PARAMETER_KNOCKDOWN: + defense->knockdown = GET_FLOAT_ARGP(2); + break; + + case DEFENSE_PARAMETER_LEGACY: + + /* + * Legacy read. Populate values from single + * line muti-parameter command. + * + * Please do not expand the legacy read. We + * want to encourage creators to use up to + * date methods. + */ + + if (arglist->count >= 2) + { + defense->factor = GET_FLOAT_ARGP(2); + } + if (arglist->count >= 3) + { + defense->pain = GET_FLOAT_ARGP(3); + } + if (arglist->count >= 4) + { + defense->knockdown = GET_FLOAT_ARGP(4); + } + if (arglist->count >= 5) + { + defense->blockpower = GET_FLOAT_ARGP(5); + } + if (arglist->count >= 6) + { + defense->blockthreshold = GET_FLOAT_ARGP(6); + } + if (arglist->count >= 7) + { + defense->blockratio = GET_FLOAT_ARGP(7); + } + if (arglist->count >= 8) + { + defense->blocktype = GET_FLOAT_ARGP(8); + } + break; + + case DEFENSE_PARAMETER_PAIN: + defense->pain = GET_FLOAT_ARGP(2); + break; + } + +} + +/* +* Caskey, Damon V. +* 2021-08-30 +* +* Free defense from memory. +*/ +void defense_free_object(s_defense* target) +{ + if (!target) + { + return; + } + + free(target); +} + +/* +* Caskey, Damon V. +* 2021-09-08 +* +* Accept animation object and frame. Returns +* the defense object from animation frame's +* collision, or NULL if not available. +*/ +//s_defense* defense_get_current_body_defense_from_animation(s_anim* animation_object, int animation_frame) +//{ +// s_collision_body* collision_body_object = NULL; +// s_body* body_object = NULL; +// +// /* Animation frame has a collision body allocated? */ +// +// collision_body_object = animation_object->collision_body[animation_frame]; +// +// if (!collision_body_object) +// { +// return NULL; +// } +// +// /* +// * Collision body should always have body +// * allocated, but let's make certain. +// */ +// +// body_object = collision_body_object->body; +// +// if (!body_object) +// { +// return NULL; +// } +// +// /* +// * Return body's defense property. In most +// * cases there won't be a defense allocated +// * for individual bodies, so this is likely +// * a NULL pointer value. Be very careful +// * when referencing downstream. +// */ +// +// return body_object->defense; +//} +// +///* +//* Caskey, Damon V. +//* 2021-09-08 +//* +//* Accept entity pointer. Returns defense object +//* from current animation frame's collision, or +//* NULL if not available. +//*/ +//s_defense* defense_get_current_body_defense_object_from_entity(entity* target_entity) +//{ +// s_anim* animation_object = NULL; +// +// /* +// * Every entity should have an animation +// * allocated, but let's make certain. +// */ +// +// animation_object = target_entity->animation; +// +// if (!animation_object) +// { +// return NULL; +// } +// +// /* +// * Send animation pointer to the defense get +// * function for animations and return its +// * result. Most animation frames won't have +// * a defense allocated, so this is likely a +// * NULL pointer value. Be very careful +// * when referencing downstream. +// */ +// +// return defense_get_current_body_defense_from_animation(animation_object, target_entity->animpos); +//} + + +/* +* Caskey, Damon V. +* 2023-02-09 +* +* Return final damage output from +* after offense adjustments. +*/ +int offense_result_damage(s_offense* offense_object, int attack_force) +{ + int result = attack_force; + + if (!offense_object) + { + return result; + } + + result = (int)(result * offense_object->factor); + + result += offense_object->damage_adjust; + + if (result < offense_object->damage_min) + { + result = offense_object->damage_min; + } + + if (result > offense_object->damage_max) + { + result = offense_object->damage_max; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2021-09-08 +* +* Return final damage output from +* after defense adjustments. If the attack +* was blocked, then the block ratio and +* block damage adjustments are applied. If +* the attack was not blocked, then the defense +* factor and damage adjustments are applied. +*/ +int64_t defense_result_damage(const s_defense* defense_object, int64_t attack_force, bool blocked) { + const float DEFENSE_GLOBAL_BLOCK_RATIO = 0.25f; + + int64_t result = attack_force; + float ratio = 0.0f; + int64_t damage_adjust = 0; + int64_t damage_min = INT64_MIN; + int64_t damage_max = INT64_MAX; + + /* + * Fall back to global default if defense + * object is NULL. + */ + const s_defense* const defense_object_local = defense_object ? defense_object : &default_defense; + + /* + * Get the ratio and adjustments for a hit + * or successfully blocked attack. + */ + if (blocked) { + if (defense_object_local->blockratio == + DEFENSE_BLOCKRATIO_COMPATABILITY_DEFAULT) { + ratio = global_config.block_ratio + ? DEFENSE_GLOBAL_BLOCK_RATIO + : 0.0f; + } else { + ratio = defense_object_local->blockratio; + } + + damage_adjust = defense_object_local->block_damage_adjust; + damage_min = defense_object_local->block_damage_min; + damage_max = defense_object_local->block_damage_max; + } else { + ratio = defense_object_local->factor; + damage_adjust = defense_object_local->damage_adjust; + damage_min = defense_object_local->damage_min; + damage_max = defense_object_local->damage_max; + } + + /* + * Apply ratio, round to the nearest whole damage + * value, apply adjustment, and enforce bounds. + */ + result = (int64_t)round((double)result * ratio); + result += damage_adjust; + + if (result < damage_min) { + result = damage_min; + } + + if (result > damage_max) { + result = damage_max; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2021-09-08 +* +* Return true if entity should enter pain reaction +* after applying defense pain property. +*/ +int defense_result_pain(s_attack* attack_object, const s_defense* defense_object) +{ + const int attack_force = attack_object->attack_force; + const attack_type_t attack_type = attack_object->attack_type; + + /* + * Make sure attack types are in bounds and defense + * object is valid. + */ + + if (!defense_object) + { + return 1; + } + + if (attack_type >= (attack_type_t)max_attack_types) + { + return 1; + } + + /* + * Defense pain 0 is default, so we don't have any + * resistance to pain. + */ + + if (!defense_object->pain) + { + return 1; + } + + /* + * Attack force meets or beats pain? Then return true + * and downstream functions will set up pain reactions. + */ + if (attack_force >= defense_object->pain) + { + return 1; + } + + /* + * Nothing passed, so defense is available and sufficient + * to resist attack force. Return false. Downstream functions + * can skip pain reaction setup. + */ + + return 0; +} + +/* +* Caskey, Damon V. +* 2021-09-01 +* +* Locate the appropriate pointer for dereferencing +* an "at the moment" defense property. Meant for +* hit logic, so we know which defense value to use. +* Returns one of the following, first to last. +* +* 1. Supplied body object's defense member. +* Presumably the body object that detected +* incoming hit. Return if defense pointer +* is valid. +* +* 2. Target entity's model level defense member. +* Return if pointer is valid. +* +* 3. Global defense_default constant. +*/ +const s_defense* defense_find_current_object(const entity* ent, const s_body* body_object, const attack_type_t attack_type) { + //printf("\n\n defense_find_current_object(%p, %p, %d)", ent, body_object, attack_type); + + /* + * Runtime attack types may include creator-defined + * values, but they must remain within the configured + * defense collection capacity. + */ + if (attack_type >= (attack_type_t)max_attack_types) { + return &default_defense; + } + + /* Supplied body. */ + + if (body_object && body_object->defense) { + //printf("\n\t &body_object->defense[%d]: %p", attack_type, &body_object->defense[attack_type]); + + return &body_object->defense[attack_type]; + } + + /* Entity defense */ + + if (ent && ent->defense) { + //printf("\n\t ent->defense: %p", &ent->defense[attack_type]); + + return &ent->defense[attack_type]; + } + + /* Global default. */ + + //printf("\n\t &default_defense: %p", &default_defense); + + return &default_defense; +} + +// attack_type_map.h + +#ifndef ATTACK_TYPE_MAP_H +#define ATTACK_TYPE_MAP_H + +typedef struct { + const char* name; + attack_type_t attack_type; +} s_attack_type_map; + +static const s_attack_type_map attack_type_map[] = { + { "NORMAL", ATK_NORMAL }, + { "NORMAL0", ATK_NORMAL }, + { "NORMAL1", ATK_NORMAL }, + { "NORMAL2", ATK_NORMAL2 }, + { "NORMAL3", ATK_NORMAL3 }, + { "NORMAL4", ATK_NORMAL4 }, + { "NORMAL5", ATK_NORMAL5 }, + { "NORMAL6", ATK_NORMAL6 }, + { "NORMAL7", ATK_NORMAL7 }, + { "NORMAL8", ATK_NORMAL8 }, + { "NORMAL9", ATK_NORMAL9 }, + { "NORMAL10", ATK_NORMAL10 }, + { "BLAST", ATK_BLAST }, + { "STEAL", ATK_STEAL }, + { "BURN", ATK_BURN }, + { "SHOCK", ATK_SHOCK }, + { "FREEZE", ATK_FREEZE }, + { "BOSS_DEATH", ATK_BOSS_DEATH }, + { "ITEM", ATK_ITEM }, + { "LAND", ATK_LAND }, + { "LIFESPAN", ATK_LIFESPAN }, + { "LOSE", ATK_LOSE }, + { "PIT", ATK_PIT }, + { "SUB_ENTITY_PARENT_KILL", ATK_SUB_ENTITY_PARENT_KILL }, + { "SUB_ENTITY_UNSUMMON", ATK_SUB_ENTITY_UNSUMMON }, + { "TIMEOVER", ATK_TIMEOVER } +}; + +#define ATTACK_TYPE_MAP_SIZE (sizeof(attack_type_map) / sizeof(attack_type_map[0])) + +#endif // ATTACK_TYPE_MAP_H + +/* + * Converts a string into a valid attack type index. + * + * Input formats supported: + * - A case-insensitive match to a predefined attack name in attack_type_map[] + * - A numeric string (e.g., "3"), interpreted as a 1-based index + * - A special case-insensitive prefix format: + * "normal" or "attack", where is an integer value + * + * Behavior: + * - The function first checks against known names in attack_type_map. + * - If not found, it looks for "attack" or "normal" prefixes and parses the suffix. + * - If still unresolved, it attempts to parse the entire input as a numeric string. + * - In all cases, the resulting attack type is adjusted to be 0-based. + * - If the final index exceeds max_attack_types or is invalid, it falls back to 0. + */ +int get_attack_type_from_string(const char* value, const char* filename) +{ + const char* prefixes[] = { "normal", "attack" }; + const size_t num_prefixes = sizeof(prefixes) / sizeof(prefixes[0]); + char normalized_input[64]; + char* endptr = NULL; + long val = -1; + int parsed_type = -1; + const char* input = value; + + //printf("\n\nParsing attack type from string '%s' (file: %s)\n", value, filename); + + // ========================================================================= + // Step 1: Normalize known prefixes OR numeric-only values into "normal" + // ========================================================================= + // This allows "attack3", "normal3", and even just "3" to resolve through LUT + // to entries like "NORMAL3". Special cases like "attack1" now work because + // "NORMAL1" is present in the LUT and maps to ATK_NORMAL. + for (size_t i = 0; i < num_prefixes; i++) { + const char* prefix = prefixes[i]; + size_t prefix_len = strlen(prefix); + + if (strnicmp(value, prefix, prefix_len) == 0) { + if (value[prefix_len] == '\0') { + // Case: "attack" or "normal" with no suffix: treat as "normal1" + snprintf(normalized_input, sizeof(normalized_input), "normal1"); + } + else if (isdigit((unsigned char)value[prefix_len])) { + // Case: "attackX" or "normalX": normalize to "normalX" + snprintf(normalized_input, sizeof(normalized_input), "normal%s", value + prefix_len); + } + else { + continue; + } + + input = normalized_input; + //printf("Normalized prefixed form '%s' to '%s'\n", value, input); + break; + } + } + + // If no prefix match occurred, check for numeric-only input + if (input == value && isdigit((unsigned char)value[0])) { + errno = 0; + val = strtol(value, &endptr, 10); + if (errno == 0 && endptr != value && *endptr == '\0' && val >= 0) { + snprintf(normalized_input, sizeof(normalized_input), "normal%ld", val); + input = normalized_input; + //printf("Normalized numeric-only string '%s' to '%s' for LUT match\n", value, input); + } + } + + // ========================================================================= + // Step 2: Lookup the normalized input in attack_type_map + // ========================================================================= + for (size_t i = 0; i < ATTACK_TYPE_MAP_SIZE; i++) { + if (stricmp(input, attack_type_map[i].name) == 0) { + //printf("Match found in attack_type_map: '%s' => %d\n", input, attack_type_map[i].attack_type); + return attack_type_map[i].attack_type; + } + } + + // ========================================================================= + // Step 3: If no LUT match, attempt to extract a number from: + // - "normal" + // - "attack" + // - plain "" + // ========================================================================= + const char* numeric_ptr = NULL; + + for (size_t i = 0; i < num_prefixes; i++) { + const char* prefix = prefixes[i]; + size_t len = strlen(prefix); + + if (strnicmp(value, prefix, len) == 0 && isdigit((unsigned char)value[len])) { + numeric_ptr = value + len; + break; + } + } + + if (!numeric_ptr && isdigit((unsigned char)value[0])) { + numeric_ptr = value; + } + + if (numeric_ptr) { + errno = 0; + val = strtol(numeric_ptr, &endptr, 10); + if (errno == 0 && endptr != numeric_ptr && *endptr == '\0' && val >= 0) { + parsed_type = (int)val; + //printf("Extracted numeric attack type: %d\n", parsed_type); + } + } + + // ========================================================================= + // Step 4: Fallback if nothing valid could be parsed + // ========================================================================= + if (parsed_type == -1) { + printf("Warning: Could not resolve attack type '%s' in file '%s'. Defaulting to ATK_NORMAL.\n", + value, filename); + return ATK_NORMAL; + } + + // ========================================================================= + // Step 5: Adjust for 1-based indexing used by designers/scripts + // ========================================================================= + if (parsed_type > 0) { + parsed_type--; + //printf("Adjusted for 1-based input: %d\n", parsed_type); + } + else { + parsed_type = 0; + //printf("Clamped type to 0 due to non-positive value.\n"); + } + + // ========================================================================= + // Step 6: Remap into user-defined attack type ID range + // ========================================================================= + const int user_input_cutoff = MAX_ATKS - STA_ATKS + 1; + if (parsed_type < user_input_cutoff) { + parsed_type = user_input_cutoff; + //printf("Input below remap threshold (%d); adjusted to %d\n", user_input_cutoff, parsed_type); + } + + parsed_type += STA_ATKS; + //printf("Remapped to user-defined attack type ID: %d\n", parsed_type); + + // ========================================================================= + // Step 7: Final bounds check against engine maximum + // ========================================================================= + if (parsed_type >= max_attack_types) { + printf("Warning: Attack type ID %d exceeds max (%d) in file '%s'. Using default.\n", + parsed_type, max_attack_types, filename); + return ATK_NORMAL; + } + + // ========================================================================= + // Step 8: Success - return resolved attack type ID + // ========================================================================= + return parsed_type; +} + + + + + +/* +* Caskey, Damon V. +* 2021-08-30 (Original) +* 2025-05-04 (Refactor) +* +* Read first argument in supplied argument list +* for type, and determines which attack type or +* attack types to assign a defense value. +*/ +void defense_setup_from_arg( + char* filename, + char* command, + const char* command_line, + s_defense* target_defense, + ArgList* arglist, + e_defense_parameters target_parameter +) { + int tempInt = 0; + int i = 0; + char* value = GET_ARGP(1); + + /* + * First try to match the value using the attack type map. + */ + for (i = 0; i < ATTACK_TYPE_MAP_SIZE; i++) + { + if (stricmp(value, attack_type_map[i].name) == 0) + { + defense_apply_setup_to_property( + filename, + command, + command_line, + &target_defense[attack_type_map[i].attack_type], + arglist, + target_parameter + ); + return; + } + } + + /* + * If no direct match, check for dynamic "normalN" pattern. + */ + if (starts_with(value, "normal")) + { + get_tail_number(tempInt, value, "normal"); + + defense_apply_setup_to_property( + filename, + command, + command_line, + &target_defense[tempInt + STA_ATKS - 1], + arglist, + target_parameter + ); + } + /* + * Handle "ALL" case - apply to all non-special attack types. + */ + else if (stricmp(value, "ALL") == 0) + { + for (i = 0; i < max_attack_types; i++) + { + if (is_attack_type_special(i)) + { + continue; + } + + defense_apply_setup_to_property( + filename, + command, + command_line, + &target_defense[i], + arglist, + target_parameter + ); + } + } +} + +/* +* Caskey, Damon V. +* 2023-02-07 +* +* Dump object data to log. +*/ +void defense_dump_object(const s_defense* object) +{ + const int space_label = 20; + + printf("\n\n -- Defense dump object (%p) Dump --\n", object); + + if (object) + { + printf("\n\t %-*s %d", space_label, "->block_damage_adjust", object->block_damage_adjust); + printf("\n\t %-*s %d", space_label, "->block_damage_max", object->block_damage_max); + printf("\n\t %-*s %d", space_label, "->block_damage_min", object->block_damage_min); + printf("\n\t %-*s %d", space_label, "->blockpower", object->blockpower); + printf("\n\t %-*s %f", space_label, "->blockratio", object->blockratio); + printf("\n\t %-*s %d", space_label, "->blockthreshold", object->blockthreshold); + printf("\n\t %-*s %d", space_label, "->blocktype", object->blocktype); + printf("\n\t %-*s %d", space_label, "->damage_adjust", object->damage_adjust); + printf("\n\t %-*s %d", space_label, "->damage_max", object->damage_max); + printf("\n\t %-*s %d", space_label, "->damage_min", object->damage_min); + printf("\n\t %-*s %d", space_label, "->death_config_flags", object->death_config_flags); + printf("\n\t %-*s %f", space_label, "->factor", object->factor); + printf("\n\t %-*s %f", space_label, "->knockdown", object->knockdown); + printf("\n\t %-*s %p", space_label, "->meta_data", object->meta_data); + printf("\n\t %-*s %d", space_label, "->meta_tag", object->meta_tag); + printf("\n\t %-*s %d", space_label, "->pain", object->pain); + } + + printf("\n\n -- Defense dump object (%p) dump complete! -- \n", object); +} + +/* +* Caskey, Damon V. +* 2023-02-07 +* +* Allocate an offense object and return pointer. +*/ +s_offense* offense_allocate_object(void) { + int attack_type; + s_offense* result = NULL; + + /* + * Allocate one extra slot because several legacy paths + * historically expect max_attack_types + 1 storage. + */ + result = calloc(max_attack_types + 1, sizeof(*result)); + + if (!result) { + borShutdown(1, E_OUT_OF_MEMORY); + } + + for (attack_type = 0; attack_type < max_attack_types + 1; attack_type++) { + result[attack_type] = default_offense; + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-02-07 +* +* Free offense from memory. +*/ +void offense_free_object(s_offense* target) +{ + if (!target) + { + return; + } + + free(target); +} + +/* +* Caskey, Damon V. +* 2023-02-07 +* +* Read first argument in supplied argument list +* for type, and determines which attack type or +* attack types to assign an offense value. +*/ +void offense_setup_from_arg( + char* filename, + char* command, + s_offense* target_offense, + ArgList* arglist, + e_offense_parameters target_parameter +) { + int tempInt = 0; + int i = 0; + char* value = GET_ARGP(1); + + /* + * First, try to match using the shared attack type map. + */ + for (i = 0; i < ATTACK_TYPE_MAP_SIZE; i++) + { + if (stricmp(value, attack_type_map[i].name) == 0) + { + offense_apply_setup_to_property( + filename, + command, + &target_offense[attack_type_map[i].attack_type], + arglist, + target_parameter + ); + return; + } + } + + /* + * Handle dynamic "normalN" pattern. + */ + if (starts_with(value, "normal")) + { + get_tail_number(tempInt, value, "normal"); + + offense_apply_setup_to_property( + filename, + command, + &target_offense[tempInt + STA_ATKS - 1], + arglist, + target_parameter + ); + } + /* + * Handle "ALL" case - apply to all non-special attack types. + */ + else if (stricmp(value, "ALL") == 0) + { + for (i = 0; i < max_attack_types; i++) + { + if (is_attack_type_special(i)) + { + continue; + } + + offense_apply_setup_to_property( + filename, + command, + &target_offense[i], + arglist, + target_parameter + ); + } + } +} + +/* +* Caskey, Damon V. +* 2023-02-07 +* +* Applies value to an attack type element +* of offense. +*/ +void offense_apply_setup_to_property(char* filename, char* command, s_offense* offense, ArgList* arglist, e_offense_parameters target_parameter) +{ + /* + * If a NULL pointer gets through, lets + * get out before we cause a NULL pointer + * error + */ + if (!offense) { + return; + } + + /* + * As of 2021-08-30, the up to date method + * sets one parameter at a time. This is more + * readable in the model text files. It also + * allows easier debug and expansion in the future. + * + * Offense is a bit clunky since we have to send + * a constant to tell us which parameter to update + * but it still beats the legacy method. + */ + + switch (target_parameter) { + case OFFENSE_PARAMETER_DAMAGE_ADJUST: + offense->damage_adjust = GET_INT_ARGP(2); + break; + + case OFFENSE_PARAMETER_DAMAGE_MAX: + offense->damage_max = GET_INT_ARGP(2); + break; + + case OFFENSE_PARAMETER_DAMAGE_MIN: + offense->damage_min = GET_INT_ARGP(2); + break; + + case OFFENSE_PARAMETER_FACTOR: + case OFFENSE_PARAMETER_LEGACY: + offense->factor = GET_FLOAT_ARGP(2); + break; + } +} + +int calculate_force_damage(entity *target, entity *attacker, s_attack *attack_object, const s_defense* defense_object, const bool blocked) { + //printf("\n\n calculate_force_damage(%p, %p, %p, %p, %d)", target, attacker, attack_object, defense_object, blocked); + + int force = attack_object->attack_force; + int type = attack_object->attack_type; + + /* + * If touch of death cheat is active, boost player damage + * to equal the target's hp. + * + * This is upstream of damage mitigation by design. Some + * games use defense to make entities immortal for special + * uses and we don't want to break that. + */ + if (global_config.cheats & CHEAT_OPTIONS_TOD_ACTIVE && attacker->modeldata.type & TYPE_PLAYER) { + if (force < target->energy_state.health_current) { + force = target->energy_state.health_current; + } + } + + /* + * Guard-break damage paths do not deal damage. This + * matches typical fighting game behavior where guard-breaks + * are meant to open up the opponent for follow-up attacks, + * not to inflict damage directly. + */ + if(!blocked && target->modeldata.guardpoints > 0 && target->guardpoints <= 0){ + return 0; + } + + //printf("\n\t force: %d", force); + + /* + * Apply the attacker's offense first. + */ + if(type >= 0 && type < max_attack_types && attacker->offense) { + force = offense_result_damage(&attacker->offense[type], force); + } + + //printf("\n\t force: %d", force); + //printf("\n\t defense_object: %p", defense_object); + //printf("\n\t defense_object->factor: %f", defense_object->factor); + + /* + * If we are not supplied a defense object, then we will + * use the target's defense property. This allows us to + * use defense from a body object if we have one, or fall + * back to model level defense if we don't. + */ + + if (!defense_object) { + defense_object = defense_find_current_object( + target, + NULL, + attack_object->attack_type + ); + } + + force = defense_result_damage(defense_object, force, blocked); + + //printf("\n\t force: %d", force); + + return force; +} + +void checkdamageonlanding(entity* acting_entity) { + + //printf("\n\n checkdamageonlanding(%p)", acting_entity); + + s_attack attack = emptyattack; + entity* other = NULL; + int didhit = 0; + + if (acting_entity->energy_state.health_current <= 0) { + return; + } + + if((acting_entity->damage_on_landing.attack_force > 0 && !(acting_entity->death_state & DEATH_STATE_DEAD))) { + + //################## + attack.attack_force = acting_entity->damage_on_landing.attack_force; + + /* + * Apply user defined type for damage on landing + * if populated. Usually there won't be one and we + * fall back to ATK_LAND. + */ + + if (attack.damage_on_landing.attack_type >= 0) { + attack.attack_type = acting_entity->damage_on_landing.attack_type; + + } else { + attack.attack_type = ATK_LAND; + } + + /* + * If we have a valid living opponent, that is + * the entity we take consider the attacker. If + * not, we use ourselves. + */ + + if (acting_entity->opponent && acting_entity->opponent->exists && !(acting_entity->opponent->death_state & DEATH_STATE_DEAD) && acting_entity->opponent->energy_state.health_current > 0) { + other = acting_entity->opponent; + + } else { + other = acting_entity; + } + + lasthit.confirm = 1; + lasthit.attack = &attack; + lasthit.position.x = acting_entity->position.x; + lasthit.position.y = acting_entity->position.y; + lasthit.position.z = acting_entity->position.z; + + const s_defense* defense_object = defense_find_current_object(acting_entity, NULL, attack.attack_type); + + //defense_dump_object(defense_object); + + // Execute the doattack functions before damage is + // processed. + execute_ondoattack_script(acting_entity, other, &attack, EXCHANGE_RECIPIANT, other->attack_id_outgoing); + execute_ondoattack_script(other, acting_entity, &attack, EXCHANGE_CONFERRER, other->attack_id_outgoing); + + //printf("\n\tother: %p", other); + //printf("\n\tattack.attack_type: %d", attack.attack_type); + //printf("\n\tattack.attack_force: %d", attack.attack_force); + + if(lasthit.confirm) { + didhit = 1; + } + + /* + * Can't take damage if we're dead. + */ + + if(acting_entity->death_state & DEATH_STATE_DEAD) { + return; + } + + /* + * Bombs detonante with their own logic when + * landing. We don't want them to take damage. + */ + + if(acting_entity->toexplode & (EXPLODE_DETONATE_HIT | EXPLODE_DETONATE_DAMAGED)) { + return; + } + + // fake 'grab', if failed, return as the attack hit nothing + if(!checkgrab(acting_entity, other, &attack)) { + return; // try to grab but failed, so return 0 means attack missed + } + + if(acting_entity != other) { + set_opponent(acting_entity, other); + } + + //################## + + if (didhit) { + + // pre-check drop + checkdamagedrop(acting_entity, &attack, defense_object); + + // Drop Weapon due to being hit. + if(acting_entity->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_LAND_DAMAGE) { + dropweapon(acting_entity, 1); + } + + // check effects, e.g., frozen, blast, steal + if(!(acting_entity->modeldata.guardpoints > 0 && acting_entity->guardpoints <= 0)) { + checkdamageeffects(acting_entity, &attack); + } + + // mprate can also control the MP recovered per hit. + checkmpadd(acting_entity); + //damage score + checkhitscore(acting_entity, other, &attack); + + /* + * Applies the damage. Send NULL as body object so + * we always use model defense or global default + * when calculating final damage. + */ + checkdamage(acting_entity, other, &attack, defense_object); + + // is it dead now? + checkdeath(acting_entity); + + execute_didhit_script(other, acting_entity, &attack, 0); + } + + if (acting_entity->energy_state.health_current <= 0) { + acting_entity->die_on_landing = 1; + } + + acting_entity->damage_on_landing.attack_force = 0; + } + + // takedamage if thrown or basted + //if( (acting_entity->damage_on_landing.attack_force > 0 && !acting_entity->death_state) && + if( (acting_entity->die_on_landing && !(acting_entity->death_state & DEATH_STATE_DEAD)) && + ((!tobounce(acting_entity) && acting_entity->modeldata.bounce) || !acting_entity->modeldata.bounce) && + (acting_entity->velocity.x == 0 && acting_entity->velocity.z == 0 && acting_entity->velocity.y == 0) + ) { + + if(acting_entity->takedamage) { + + //################## + s_attack attack = emptyattack; + entity *other; + + attack.attack_force = acting_entity->damage_on_landing.attack_force; + if (attack.damage_on_landing.attack_type >= 0) { + attack.attack_type = acting_entity->damage_on_landing.attack_type; + + } else { + attack.attack_type = ATK_LAND; + } + + if (acting_entity->opponent && acting_entity->opponent->exists && !(acting_entity->opponent->death_state & DEATH_STATE_DEAD) && acting_entity->opponent->energy_state.health_current > 0) { + other = acting_entity->opponent; + + } else { + other = acting_entity; + } + //################## + + const s_defense* other_defense_object = defense_find_current_object(other, NULL, attack.attack_type); + + acting_entity->takedamage(acting_entity, other, &attack, 1, other_defense_object); + + } else { + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_DAMAGE_ON_LANDING); + } + + if (acting_entity) { + acting_entity->damage_on_landing.attack_force = 0; + acting_entity->damage_on_landing.attack_type = ATK_NONE; + acting_entity->die_on_landing = 0; + } + } + + return; +} + +/* +* Caskey, Damon V. +* 2025-05-09 +* +* LUT of "special" attack types, as +* in, types normally for internal engine +* use only. +*/ +static const int special_attack_table[MAX_ATKS] = { + [ATK_BOSS_DEATH] = 1, + [ATK_ITEM] = 1, + [ATK_LIFESPAN] = 1, + [ATK_LOSE] = 1, + [ATK_SUB_ENTITY_PARENT_KILL] = 1, + [ATK_SUB_ENTITY_UNSUMMON] = 1, + [ATK_TIMEOVER] = 1, + [ATK_PIT] = 1, +}; + +/* +* Caskey, Damon V. +* 2019-12-26 +* +* Return true if attack type is one of the +* types not included in normal use by creators. +*/ +int is_attack_type_special(attack_type_t type) +{ + /* + * Anything outside of bounds is + * user defined and not special. + */ + if (type >= MAX_ATKS) { + return 0; + } + + /* + * Check vs. LUT of special types. + */ + return special_attack_table[type]; +} + +/* +* Original by unknown author, unknown date. +* +* Applies HP damage to target entity. Executes +* appropriate damage and death scripts as needed. +* +* Caskey, Damon V. +* 2021-09-07 +* +* Slightly reworked to accept the target entity +* as parameter rather than referencing the +* global "self" variable. +* +* Accepts defense object to pass on into +* total damage calculation functions. +*/ +void checkdamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, const s_defense* defense_object) +{ + //printf("\n\n checkdamage(%p, %p, %p, %p)", target_entity, attacking_entity, attack_object, defense_object); + + int64_t force = 0; + + /* Get attack damage force after defense is applied. */ + force = calculate_force_damage(target_entity, attacking_entity, attack_object, defense_object, false); + + /* + * Damage does not return HP and comes from + * a normal source? + */ + const bool normal_damage = (!is_attack_type_special(attack_object->attack_type) && force >= 0) ? true : false; + + /* + * If we're invincible to normal damage + * sources, laugh it off. + */ + if (target_entity->invincible & INVINCIBLE_HP_NULLIFY && normal_damage) { + force = 0; + } + + /* + * Apply the damage. In the case of negative + * damage (IOW, giving back HP), cap at + * maximum health. + * + * If attack has no kill flag enable, don't + * fall below 1 hit point. + */ + + target_entity->energy_state.health_current -= force; + + if (target_entity->energy_state.health_current > target_entity->modeldata.health) { + target_entity->energy_state.health_current = target_entity->modeldata.health; + } + + if(attack_object->no_kill && target_entity->energy_state.health_current <= 0) { + target_entity->energy_state.health_current = 1; + } + + /* Execute the take damage script. */ + execute_takedamage_script(target_entity, attacking_entity, attack_object); + + /* + * Attack meant to put health at 0? We execute + * death script, but may reset HP according + * to invincibility flags. + */ + if (target_entity->energy_state.health_current <= 0) { + + if(normal_damage) { + if (target_entity->invincible & INVINCIBLE_HP_MINIMUM) { + target_entity->energy_state.health_current = 1; + } + + if(target_entity->invincible & INVINCIBLE_HP_RESET) { + target_entity->energy_state.health_current = target_entity->modeldata.health; + } + } + + execute_ondeath_script(target_entity, attacking_entity, attack_object); + } +} + +int checkgrab(entity* target_entity, entity* attacking_entity, s_attack* attack_object) +{ + //if(attack->no_pain) return 0; //no effect, let modders to deside, don't bother check it here + if(target_entity != attacking_entity && attack_object->grab && check_cangrab(attacking_entity, target_entity)) + { + if(adjust_grabposition(attacking_entity, target_entity, attack_object->grab_distance, attack_object->grab)) + { + ents_link(attacking_entity, target_entity); + target_entity->position.y = attacking_entity->position.y; + } + else + { + return 0; + } + } + return 1; +} + +int arrow_takedamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, int fall_flag, const s_defense* defense_object) +{ + target_entity->modeldata.move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + target_entity->modeldata.move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL); + + if(common_takedamage(target_entity, attacking_entity, attack_object, 0, defense_object) + && target_entity->death_state & DEATH_STATE_DEAD) + { + return 1; + } + return 0; +} + +int common_takedamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, int fall_flag, const s_defense* defense_object) +{ + int pain_check = 0; // React with pain animations (1) or ignore (0); + e_death_config_flags death_config; + + /* + * Resolve callers that do not supply a body-specific + * defense object through the target's current defense. + */ + if (!defense_object) { + defense_object = defense_find_current_object( + target_entity, + NULL, + attack_object->attack_type + ); + } + + if(target_entity->death_state & DEATH_STATE_DEAD) + { + return 0; + } + + if(target_entity->toexplode & (EXPLODE_DETONATE_HIT | EXPLODE_DETONATE_DAMAGED)) + { + return 0; + } + + // fake 'grab', if failed, return as the attack hit nothing + if(!checkgrab(target_entity, attacking_entity, attack_object)) + { + return 0; // try to grab but failed, so return 0 means attack missed + } + + // set oppoent + if(target_entity != attacking_entity) + { + set_opponent(target_entity, attacking_entity); + } + + // adjust type + if(attack_object->attack_type >= 0 && attack_object->attack_type < max_attack_types) + { + target_entity->last_damage_type = attack_object->attack_type; + } + else + { + target_entity->last_damage_type = ATK_NORMAL; + } + + if (!target_entity->die_on_landing) + { + // pre-check drop + checkdamagedrop(target_entity, attack_object, defense_object); + + // Drop Weapon due to being hit. + if(target_entity->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_DAMAGE) + { + dropweapon(target_entity, 1); + } + // check effects, e.g., frozen, blast, steal + if(!(target_entity->modeldata.guardpoints > 0 && target_entity->guardpoints <= 0)) + { + checkdamageeffects(target_entity, attack_object); + } + } + + // check backpain + check_backpain(attacking_entity, target_entity); + + /* Check and apply direction flip. */ + checkdamageflip(target_entity, attacking_entity, attack_object, defense_object); + + if (!target_entity->die_on_landing) + { + // mprate can also control the MP recovered per hit. + checkmpadd(target_entity); + //damage score + checkhitscore(target_entity, attacking_entity, attack_object); + + // check damage, cost hp. + checkdamage(target_entity, attacking_entity, attack_object, defense_object); + + // is it dead now? + checkdeath(target_entity); + } + + if(target_entity->modeldata.type & TYPE_PLAYER) + { + if (savedata.joyrumble[target_entity->playerindex]) control_rumble(target_entity->playerindex, 1, attack_object->attack_force * 3); + } + if(target_entity->position.y <= PIT_DEPTH && target_entity->death_state & DEATH_STATE_DEAD) + { + if(target_entity->modeldata.type & TYPE_PLAYER) + { + player_die_entity(target_entity); + } + else + { + kill_entity(target_entity, KILL_ENTITY_TRIGGER_TAKE_DAMAGE_COMMON_PIT); + } + return 1; + } + + // fall to the ground so don't fall again + /*if(acting_entity->damage_on_landing.attack_force) + { + acting_entity->damage_on_landing.attack_force = 0; + return 1; + }*/ + // reset damageonlanding + target_entity->damage_on_landing.attack_force = 0; + target_entity->damage_on_landing.attack_type = ATK_NONE; + + // White Dragon: fix damage_on_landing bug + if(target_entity->die_on_landing && target_entity->energy_state.health_current <= 0) + { + target_entity->modeldata.death_config_flags |= DEATH_CONFIG_MACRO_DEATH_FALL_LAND; + } + + // unlink due to being hit + if((target_entity->opponent && target_entity->opponent->grabbing != target_entity) // Have an opponent, but opponent is not grabbing me. + || target_entity->death_state & DEATH_STATE_DEAD // Dead. + || target_entity->frozen // Frozen. + || target_entity->drop) // Knocked down. + { + ent_unlink(target_entity); + } + // Enemies can now use SPECIAL2 to escape cheap attack strings! + if(target_entity->modeldata.escapehits) + { + if(target_entity->drop) + { + target_entity->escapecount = 0; + } + else + { + target_entity->escapecount++; + } + } + + /* + * Handle Pain, fall, and death animations. + * May also not react at all if attack doesn't + * cause pain or defense is enough to ignore it. + */ + pain_check = defense_result_pain(attack_object, defense_object); + + if(target_entity->drop || target_entity->energy_state.health_current <= 0) + { + target_entity->takeaction = common_fall; + + // Drop Weapon due to death. + if(target_entity->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_DEATH && target_entity->energy_state.health_current <= 0) + { + dropweapon(target_entity, 1); + } + else if(target_entity->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_FALL) + { + dropweapon(target_entity, 1); + } + + death_config = defense_object->death_config_flags; + + if (death_config & DEATH_CONFIG_SOURCE_MODEL) + { + death_config = target_entity->modeldata.death_config_flags; + } + + /* We're alive, or death sequence wants us to handle falling. */ + if(target_entity->energy_state.health_current > 0 || !death_try_sequence_damage(target_entity, death_config, DEATH_TRY_SEQUENCE_ACTING_EVENT_DAMAGE)) + { + if (fall_flag >= 1) return 1; + target_entity->velocity.x = attack_object->dropv.x; + target_entity->velocity.z = attack_object->dropv.z; + if(target_entity->direction == DIRECTION_RIGHT) + { + target_entity->velocity.x = -target_entity->velocity.x; + } + if(target_entity->inbackpain) target_entity->velocity.x *= -1; + toss(target_entity, attack_object->dropv.y); + target_entity->damage_on_landing.attack_force = attack_object->damage_on_landing.attack_force; + target_entity->damage_on_landing.attack_type = attack_object->damage_on_landing.attack_type; + target_entity->knockdowncount = target_entity->modeldata.knockdowncount; // reset the knockdowncount + target_entity->knockdowntime = 0; + + // If no fall/die animations exist, entity simply disappears. + if(!set_fall(target_entity, attacking_entity, attack_object, 1)) + { + if(target_entity->modeldata.type & TYPE_PLAYER) + { + player_die_entity(target_entity); + } + else + { + kill_entity(target_entity, KILL_ENTITY_TRIGGER_TAKE_DAMAGE_COMMON_FALL); + } + return 1; + } + } + + if(target_entity->modeldata.type & TYPE_PLAYER) + { + if (savedata.joyrumble[target_entity->playerindex]) control_rumble(target_entity->playerindex, 1, attack_object->attack_force * 3); + } + } + else if(attack_object->grab && !attack_object->no_pain) + { + target_entity->takeaction = common_pain; + attacking_entity->takeaction = common_grabattack; + attacking_entity->stalltime = _time + global_config.grab_stall; + target_entity->releasetime = _time + (global_config.game_speed / 2); + set_pain(target_entity, target_entity->last_damage_type, 0); + } + // Don't change to pain animation if frozen + else if(!target_entity->frozen && !(target_entity->modeldata.pain_config_flags & PAIN_CONFIG_PAIN_DISABLE) && !attack_object->no_pain && pain_check) + { + target_entity->takeaction = common_pain; + set_pain(target_entity, target_entity->last_damage_type, 1); + } + + return 1; +} + +int common_try_runattack(entity *target) +{ + if(!self->running || !validanim(self, ANI_RUNATTACK)) + { + return 0; + } + + + if(!target) + { + target = normal_find_target(ANI_RUNATTACK, 0); + } + + if(target) + { + if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE)) + { + return 0; + } + self->takeaction = common_attack_proc; + self->velocity.z = self->velocity.x = 0; + set_attacking(self); + ent_set_anim(self, ANI_RUNATTACK, 0); + return 1; + } + return 0; +} + +// Active blocking enabled. +// +// AI can behave more like players when blocking. Normally AI +// blocking is passive. IOW, it can only choose to block attacks +// as they hit. This function allows the AI to initiate blocking +// preemptively the way players have to. +// +// AI blocks if following conditions are met: +// +// 1. Entity has active block enabled. +// 2. Target is within range of BLOCK animation. +// 3. Target is actively attacking. +// 4. Blocking chance passes (same rules as active blocking). +static bool common_try_block(entity *target) { + + /* Only active AI may initiate blocking before impact. */ + if (!(self->modeldata.block_config_flags & BLOCK_CONFIG_ACTIVE) + || (self->modeldata.block_config_flags & BLOCK_CONFIG_DISABLED)) { + return false; + } + + // Must have block animation. + if (!validanim(self, ANI_BLOCK)) { + return false; + } + + // Active blocking is enabled. Exit if the chance roll fails. + if (!check_blocking_decision(self)) { + return false; + } + + // If no current target, use block range. + if(!target) { + target = block_find_target(ANI_BLOCK, 0); + } + + // Still no target? Nothing to do, so exit. + if (!target) { + return false; + } + + // If target is attacking, let's block and return true. + if(target->attacking != ATTACKING_NONE) { + // Set up flags, action, and blocking animations. + do_active_block(self); + + return true; + } + + return false; +} + +// this logic could be used for multiple times, so make a function +// pick a random attack or return the first attacks if testonly is set +// when testonly is set, the function will not check special attacks (upper, jumpattack) +// if target is NULL, ranges are not checked +int pick_random_attack(entity *target, int testonly) +{ + int found = 0, i, j; + + for(i = 0; i < max_attacks; i++) // TODO: recheck range for attacks chains + { + if(validanim(self, animattacks[i]) && + (!target || check_range_target_all(self, target, animattacks[i], 0, 0))) + { + for(j = ((5 - i) >= 0 ? (5 - i) : 0) * 3; j >= 0; j--) + { + ai_attack_choice_append(&found, animattacks[i]); + } + } + } + for(i = 0; i < max_freespecials; i++) + { + if(validanim(self, animspecials[i]) && + (check_energy(ENERGY_TYPE_MP, animspecials[i]) || + check_energy(ENERGY_TYPE_HP, animspecials[i])) && + (!target || check_range_target_all(self, target, animspecials[i], 0, 0))) + { + ai_attack_choice_append(&found, animspecials[i]); + } + } + if( validanim(self, ANI_THROWATTACK) && + self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && + (!target || check_range_target_all(self, target, ANI_THROWATTACK, 0, 0) )) + { + ai_attack_choice_append(&found, ANI_THROWATTACK); + } + + if(testonly) + { + if(found) + { + return ai_attack_choices[(rand32() & 0xffff) % found]; + } + return -1; + } + + if( validanim(self, ANI_JUMPATTACK) && + (!target || check_range_target_all(self, target, ANI_JUMPATTACK, 0, 0)) ) + { + if(testonly) + { + return ANI_JUMPATTACK; + } + ai_attack_choice_append(&found, ANI_JUMPATTACK); + } + if( validanim(self, ANI_UPPER) && + (!target || check_range_target_all(self, target, ANI_UPPER, 0, 0)) ) + { + if(testonly) + { + return ANI_UPPER; + } + ai_attack_choice_append(&found, ANI_UPPER); + } + + if(found) + { + return ai_attack_choices[(rand32() & 0xffff) % found]; + } + + return -1; +} + + +// code to lower the chance of attacks, may change while testing old mods +// min - min attack chance +// max - max attack chance +int check_attack_chance(entity *target, float min, float max) +{ + + float chance, chance1, chance2;//, aggfix; + + if(self->modeldata.aiattack & AIATTACK1_ALWAYS) + { + return 1; + } + + chance1 = MIN(1.0f, (diff(self->position.x, self->destx) + diff(self->position.z, self->destz)) / (videomodes.hRes + videomodes.vRes) * move_noatk_factor); + chance2 = MIN(1.0f, (count_ents(self->modeldata.type) - 1) * group_noatk_factor); + + chance = (1.0f - chance1) * (1.0f - chance2); + + if(chance > max) + { + chance = max; + } + else if(chance < min) + { + chance = min; + } + + chance *= (1.0 - self->modeldata.attackthrottle); + + if(self->position.x < screenx - 10 || self->position.x > screenx + videomodes.hRes + 10) + { + if (self->modeldata.offscreen_noatk_factor != 0) chance *= (1.0 - self->modeldata.offscreen_noatk_factor); + else chance *= (1.0 - offscreen_noatk_factor); + } + + return (randf(1) <= chance); +} + +//make a function, mostly for debug purpose +//give it a chance to reset current noattack timer +u32 recheck_nextattack(entity *target) +{ + if(target->blocking & BLOCK_STATE_ACTIVE) + { + self->nextattack = 0; + } + else if(target->attacking != ATTACKING_NONE && self->nextattack > 4) + { + self->nextattack -= 4; + } + else if(target->jumping && self->nextattack > 16) + { + self->nextattack -= 16; + } + + return self->nextattack; +} + +int common_try_normalattack(entity *target) +{ + target = normal_find_target(-1, 0); + + if(!target) + { + return 0; + } + + recheck_nextattack(target); + + if(recheck_nextattack(target) > _time) + { + return 0; + } + + if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE || target->takeaction == common_rise)) + { + return 0; + } + + if(pick_random_attack(target, 1) >= 0) + { + if(self->combostep[0] && self->combotime > _time) + { + self->stalltime = _time + 1; + } + else + { + if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance)) + { + self->nextattack = _time + randf(self->modeldata.attackthrottletime); + return 0; + } + else + { + self->stalltime = _time + (int)randf((float)MAX(1, global_config.game_speed * 3 / 4 - self->modeldata.aggression)); + } + } + + self->takeaction = normal_prepare; + self->velocity.z = self->velocity.x = 0; + set_idle(self); + self->idling = IDLING_NONE; // not really idle, in fact it is thinking + self->attacking = ATTACKING_PREPARED; // pre-attack, for AI-block check + return 1; + } + + return 0; +} + +int common_try_jumpattack(entity *target) +{ + entity *dust; + int rnum, ani = 0; + if((validanim(self, ANI_JUMPATTACK) || validanim(self, ANI_JUMPATTACK2))) + { + if(!validanim(self, ANI_JUMPATTACK)) + { + rnum = 1; + } + else if(validanim(self, ANI_JUMPATTACK2) && (rand32() & 1)) + { + rnum = 1; + } + else + { + rnum = 0; + } + + if(rnum == 0 && + // do a jumpattack + (target || (target = normal_find_target(ANI_JUMPATTACK, 0))) ) + { + if(recheck_nextattack(target) > _time) + { + return 0; + } + + if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE)) + { + rnum = -1; + } + else + { + if(!check_attack_chance(target, 0.05f, 0.4f)) + { + self->nextattack = _time + randf(self->modeldata.attackthrottletime); + return 0; + } + //ent_set_anim(self, ANI_JUMPATTACK, 0); + ani = ANI_JUMPATTACK; + if(self->direction == DIRECTION_RIGHT) + { + self->velocity.x = (float)1.3; + } + else + { + self->velocity.x = (float) - 1.3; + } + self->velocity.z = 0; + } + } + else if(rnum == 1 && + // do a jumpattack2 + (target || (target = normal_find_target(ANI_JUMPATTACK2, 0))) ) + { + if(recheck_nextattack(target) > _time) + { + return 0; + } + + if(!target->animation->vulnerable[target->animpos] && (target->drop || target->attacking != ATTACKING_NONE)) + { + rnum = -1; + } + else + { + if(!check_attack_chance(target, 0.05f, 0.5f)) + { + self->nextattack = _time + randf(self->modeldata.attackthrottletime); + return 0; + } + //ent_set_anim(self, ANI_JUMPATTACK2, 0); + ani = ANI_JUMPATTACK2; + self->velocity.x = self->velocity.z = 0; + } + } + else + { + rnum = -1; + } + + if(rnum >= 0) + { + + self->takeaction = common_jump; + set_attacking(self); + self->jumping = 1; + toss(self, self->modeldata.jumpheight); + + if(self->modeldata.dust.jump_start >= 0) + { + dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL); + if(dust) + { + dust->spawntype = SPAWN_TYPE_DUST_JUMP; + dust->base = self->position.y; + dust->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(dust); + } + } + + ent_set_anim(self, ani, 0); + + return 1; + } + } + + return 0; +} + +int common_try_grab(entity *other) +{ + //printf("\n\t common_try_grab(%p)", other); + + int trygrab(entity * t); + + /* + * Note order does matter in the + * checks below. usually we don't + * have an "other" until it is + * populated by find_ent_here. + */ + + // old rand() + if( (rand32() & 7) == 0 && + (validanim(self, ANI_THROW) || validanim(self, ANI_GRAB)) && + self->idling && + (other || (other = find_ent_here(self, self->position.x, self->position.z, TYPE_ANY, NULL))) && + faction_check_is_hostile(self, other) && + trygrab(other)) + { + return 1; + } + + return 0; +} + +// A.I. try upper cut +int common_try_upper(entity *target) +{ + if(!validanim(self, ANI_UPPER)) + { + return 0; + } + + if(!target) + { + target = normal_find_target(ANI_UPPER, 0); + } + + // Target jumping? Try uppercut! + if(target && target->jumping) + { + /*self->takeaction = common_attack_proc; + set_attacking(self); + self->velocity.z = self->velocity.x = 0; + // Don't waste any time! + ent_set_anim(self, ANI_UPPER, 0);*/ + + if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance)) + { + self->nextattack = _time + randf(self->modeldata.attackthrottletime); + return 0; + } + else + { + self->stalltime = _time + (int)randf((float)MAX(1, global_config.game_speed * 3 / 4 - self->modeldata.aggression)); + } + + self->takeaction = upper_prepare; + self->velocity.z = self->velocity.x = 0; + set_idle(self); + self->idling = IDLING_NONE; // not really idle, in fact it is thinking + self->attacking = ATTACKING_PREPARED; // pre-attack, for AI-block check + return 1; + } + + return 0; +} + +int common_try_duckattack(entity *other) +{ + int indirect; + entity *target = NULL; + + if(!validanim(self, ANI_DUCKATTACK) || !(self->ducking & DUCK_ACTIVE)) + { + return 0; + } + + if(_time / THINK_SPEED % 4 == 0) + { + return 0; + } + + if(self->projectile != BLAST_NONE) + { + indirect = 1; + } + + if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0); + else target = self->custom_target; + + if(target && !(faction_check_can_damage(self, target, indirect))) + { + return 0; + } + + if(recheck_nextattack(target) > _time) + { + return 0; + } + + if(!target || !(target->ducking & DUCK_ACTIVE)) + { + return 0; + } + + if(!check_attack_chance(target, 1.0f - min_noatk_chance, 1.0f - min_noatk_chance)) + { + self->nextattack = _time + randf(self->modeldata.attackthrottletime); + return 0; + } + else + { + self->stalltime = _time + (int)randf((float)MAX(1, global_config.game_speed * 3 / 4 - self->modeldata.aggression)); + } + + // finally attack! + self->takeaction = common_attack_proc; + set_attacking(self); + self->velocity.z = self->velocity.x = 0; + // Don't waste any time! + ent_set_anim(self, ANI_DUCKATTACK, 0); + + return 1; +} + +// Normal attack style +// Used by root A.I., what to do if a target is found. +// return 0 if no action is token +// return 1 if an action is token +int normal_attack() +{ + //int rnum; + + //rnum = rand32()&7; + if( common_try_duckattack(NULL) || + common_try_grab(NULL) || + common_try_upper(NULL) || + common_try_block(NULL) || + common_try_runattack(NULL) || + //(rnum < 2 && common_try_freespecial(NULL)) || + common_try_normalattack(NULL) || + common_try_jumpattack(NULL) ) + { + self->running = RUN_STATE_NONE; + return 1; + } + + return 0;// nothing to do? so go to next think step +} + +// A.I. characters do a throw +void common_throw() +{ + if(self->animating) + { + return; // just play the throw animation + } + + // we have done the throw, return to A.I. root + self->takeaction = NULL; + + set_idle(self); +} + +// toss the grabbed one +void dothrow() +{ + s_attack attack = emptyattack; + entity *other; + self->velocity.x = self->velocity.z = 0; + other = self->link; + if(other == NULL) //change back to idle, or we will get stuck here + { + self->takeaction = NULL;// A.I. root again + set_idle(self); + return; + } + + if(other->modeldata.throwheight) + { + toss(other, other->modeldata.throwheight); + } + else + { + toss(other, other->modeldata.jumpheight); + } + + other->direction = self->direction; + other->projectile |= BLAST_TOSS; + other->projectile |= BLAST_ATTACK; + other->velocity.x = (other->direction == DIRECTION_RIGHT) ? (-other->modeldata.throwdist) : (other->modeldata.throwdist); + + if(autoland == 1 && validanim(other, ANI_LAND)) + { + other->damage_on_landing.attack_force = ATTACK_FORCE_LAND_AUTO; + } + else + { + other->damage_on_landing.attack_force = self->modeldata.throwdamage; + } + + ent_unlink(other); + + other->takeaction = common_fall; + self->takeaction = common_throw; + + // Use default attack values. + set_fall(other, self, &attack, 0); + ent_set_anim(self, ANI_THROW, 0); +} + + +// Waiting until throw frame reached +void common_throw_wait() +{ + if(!self->link) + { + self->takeaction = NULL;// A.I. root again + set_idle(self); + return; + } + + self->releasetime += THINK_SPEED; //extend release time + + if(self->animpos != self->modeldata.throwframewait) + { + return; + } + + dothrow(); +} + + +void common_prethrow() +{ + self->running = RUN_STATE_NONE; // Quits running if grabbed by opponent + + // Just check if we're still grabbed... + if(self->link) + { + return; + } + + self->takeaction = NULL;// A.I. root again + + set_idle(self); +} + +/* +* Caskey, Damon V. +* 2023-05-24 (rewrite of orginal function by Utunnels, unknown date) +* +* Reposition entity to its parent's location +* and play appropriate animation. +*/ +void npc_recall() +{ + entity* const acting_entity = self; + + const s_child_follow* const child_follow = acting_entity->modeldata.child_follow; + + if(!child_follow || !acting_entity->parent) + { + return; + } + + acting_entity->base = acting_entity->parent->base; + acting_entity->position.x = acting_entity->parent->position.x + (acting_entity->parent->direction == DIRECTION_RIGHT ? child_follow->follow_offset.x : -child_follow->follow_offset.x); + acting_entity->position.y = acting_entity->parent->position.y + child_follow->recall_offset.y; + acting_entity->position.z = acting_entity->parent->position.z + child_follow->recall_offset.z; + + acting_entity->velocity.x = 0; + acting_entity->velocity.y = 0; + acting_entity->velocity.z = 0; + + if (child_follow->recall_animation != ANI_NONE) + { + if (validanim(acting_entity, child_follow->recall_animation)) + { + acting_entity->takeaction = common_spawn; + ent_set_anim(acting_entity, child_follow->recall_animation, 0); + } + else if (validanim(acting_entity, ANI_SPAWN)) + { + acting_entity->takeaction = common_spawn; + ent_set_anim(acting_entity, ANI_SPAWN, 0); + } + } +} + +int adjust_grabposition(entity *ent, entity *other, float dist, int grabin) +{ + float x1; + float z1; + float x2; + float z2; + float x; + + if(diff(ent->position.y,other->position.y) > T_WALKOFF) + { + return 0; + } + + if(diff(ent->base,other->base) > T_WALKOFF) + { + return 0; + } + + if(grabin == 1) + { + x1 = ent->position.x; + z1 = z2 = ent->position.z; + x2 = ent->position.x + ((other->position.x > ent->position.x) ? dist : -dist); + } + else + { + x = (ent->position.x + other->position.x) / 2; + x1 = x + ((ent->position.x >= other->position.x) ? (dist / 2) : (-dist / 2)); + x2 = x + ((other->position.x > ent->position.x) ? (dist / 2) : (-dist / 2)); + z1 = z2 = (ent->position.z + other->position.z) / 2; + } + + if(0 >= testmove(ent, ent->position.x, ent->position.z, x1, z1) || 0 >= testmove(other, other->position.x, other->position.z, x2, z2)) + { + return 0; + } + + ent->position.x = x1; + ent->position.z = z1; + other->position.x = x2; + other->position.z = z2; + //other->position.y = ent->position.y; + //other->base = ent->base; + + // Sort order control. + // + // If grabback is set (grabback = 1) the grabbed entity's + // sort order is forced 1 greater than grappler so grappler + // appears behind. Otherwise grabbed is moved one lower, + // forcing grappler to appear in front. + if (ent->modeldata.grabback) + { + other->sortid = ent->sortid + 1; + } + else if (!ent->modeldata.grabback) + { + other->sortid = ent->sortid - 1; + } + + + return 1; +} + +int dograb(entity *attacker, entity *target, e_dograb_adjustcheck adjustcheck) +{ + /* + Execute grab action. Added by splitting off trygrab + so entities can be forced to perform grab by script. + Damon V. Caskey + 2013-12-30 + + Attacker: Entity performing grab. + target, entity being grabbed. + */ + + int result = 0; //Output value. + int pass = 1; //Adjust pass/fail. + + /* If an adjust check is needed, make sure adjusting did not fail. */ + if(adjustcheck == DOGRAB_ADJUSTCHECK_TRUE) + { + pass = adjust_grabposition(attacker, target, attacker->modeldata.grabdistance, 0); + } + + /* If adjust_grabposition passed (or wasn't needed) perform grab actions. */ + if(pass) + { + if(attacker->modeldata.grabflip & 1) // Kratus (10-2021) Make grabflip property accessible by script + { + attacker->direction = (attacker->position.x < target->position.x); + } + + /* Set flags. */ + set_opponent(target, attacker); + ents_link(attacker, target); + target->attacking = ATTACKING_NONE; + attacker->idling = IDLING_NONE; + attacker->running = RUN_STATE_NONE; + attacker->ducking = DUCK_NONE; + attacker->inbackpain = 0; + + /* Stop all movement. */ + attacker->velocity.x = 0; + attacker->velocity.z = 0; + target->velocity.x = 0; + target->velocity.z = 0; + + /* Check for grab animation, otherwise use original throwing system. */ + if(validanim(self, ANI_GRAB)) + { + if(attacker->modeldata.grabflip & 2) // Kratus (10-2021) Make grabflip property accessible by script + { + target->direction = !attacker->direction; + } + attacker->attacking = ATTACKING_NONE; + memset(attacker->combostep, 0, 5 * sizeof(*attacker->combostep)); + target->stalltime = _time + global_config.grab_stall; + attacker->releasetime = _time + (global_config.game_speed / 2); + target->takeaction = common_grabbed; + attacker->takeaction = common_grab; + ent_set_anim(attacker, ANI_GRAB, 0); + set_pain(target, -1, 0); //set grabbed animation + } + else + { + /* + If no throwframewait use original throw code immediately. + Otherwise use throwframewait. + */ + if(attacker->modeldata.throwframewait == -1) + { + dothrow(); + } + else + { + if(attacker->modeldata.grabflip & 2) // Kratus (10-2021) Make grabflip property accessible by script + { + target->direction = !attacker->direction; + } + + target->takeaction = common_prethrow; + attacker->takeaction = common_throw_wait; + ent_set_anim(attacker, ANI_THROW, 0); + set_pain(target, -1, 0); // set grabbed animation + } + } + + result = 1; + } + + return result; +} + +int trygrab(entity *other) +{ + /* + Actions after grab possibility check moved to dograb. + Damon V. Caskey + 2013-12-30 + + other: Grab target. + */ + + int result = 0; //return value. + + if(check_cangrab(self, other)) + { + result = dograb(self, other, DOGRAB_ADJUSTCHECK_TRUE); + } + + return result; +} + +int common_trymove(float xdir, float zdir) +{ + entity *other = NULL, *te = NULL; + int wall, heightvar, t, needcheckhole = 0; + float x, z, oxdir, ozdir; + + if(!xdir && !zdir) + { + return 0; + } + + // UT: move player grab logic here to bypass some extra checks + // it used to be at the very end of this function + //------------------ grab/throw checking ------------------ + + //printf("rand(): %d rand32(): %d\n",rand(),rand32()); + // old rand() + if((self->modeldata.type & TYPE_PLAYER) && + (rand32() & 7) == 0 && + (validanim(self, ANI_THROW) || + validanim(self, ANI_GRAB)) && self->idling && + (other = find_ent_here(self, self->position.x, self->position.z, self->faction.type_hostile, NULL))) + { + if(trygrab(other)) + { + return 0; + } + } + // --------------- end of grab/throw checking ------------------------ + + + oxdir = xdir; + ozdir = zdir; + /* + // entity is grabbed by other + if(self->link && self->link->grabbing==self && self->link->grabwalking) + { + return 1; // just return so we don't have to check twice + }*/ + + x = self->position.x + xdir; + z = self->position.z + zdir; + // -----------bounds checking--------------- + // Subjec to Z and out of bounds? Return to level! + if (self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MIN_Z) + { + if(z < PLAYER_MIN_Z) + { + zdir = PLAYER_MIN_Z - self->position.z; + execute_onblockz_script(self); + } + } + + if (self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MAX_Z) + { + if(z > PLAYER_MAX_Z) + { + zdir = PLAYER_MAX_Z - self->position.z; + execute_onblockz_script(self); + } + } + + // screen checking + if(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_SCREEN) + { + if(x < advancex + 10) + { + xdir = advancex + 10 - self->position.x; + execute_onblocks_script(self); //Screen block event. + } + else if(x > advancex + (videomodes.hRes - 10)) + { + xdir = advancex + (videomodes.hRes - 10) - self->position.x; + execute_onblocks_script(self); //Screen block event. + } + } + + if(!xdir && !zdir) + { + return 0; + } + x = self->position.x + xdir; + z = self->position.z + zdir; + + //-----------end of bounds checking----------- + + //-------------hole checking --------------------- + // Don't walk into a hole or walk off platforms into holes + if( self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_HOLE && !inair(self) && !(self->modeldata.type & TYPE_PLAYER) && self->idling && + (!self->animation->move[self->animpos]->base || self->animation->move[self->animpos]->base < 0) && + !(self->modeldata.aimove & AIMOVE2_IGNOREHOLES)) + { + + needcheckhole = 1; + if(zdir && checkhole(self->position.x, z) && checkwall_index(self->position.x, z) < 0 && !check_platform_below(self->position.x, z, self->position.y, self)) + { + //int holeind = checkholeindex_in(self->position.x, z, self->position.y); + + zdir = 0; + //execute_inhole_script(self, 2, (double)level->holes[holeind].height, holeind); + } + if(xdir && checkhole(x, self->position.z) && checkwall_index(x, self->position.z) < 0 && !check_platform_below(x, self->position.z, self->position.y, self)) + { + //int holeind = checkholeindex_in(x, self->position.z, self->position.y); + + xdir = 0; + //execute_inhole_script(self, 1, (double)level->holes[holeind].height, holeind); + } + } + + if(!xdir && !zdir) + { + return 0; + } + x = self->position.x + xdir; + z = self->position.z + zdir; + //-----------end of hole checking--------------- + + //--------------obstacle checking ------------------ + if(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_OBSTACLE /*&& !inair(self)*/) + { + int hit = 0; + + //TODO, check once instead of twice + if((other = find_ent_here(self, x, self->position.z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) && + (xdir > 0 ? other->position.x >= self->position.x : other->position.x <= self->position.x) && + (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT])) + { + xdir = 0; + if ( self->falling ) hit |= 1; + te = other; + execute_onblocko_script(self, PLANE_X, other); + } + if((other = find_ent_here(self, self->position.x, z, (TYPE_OBSTACLE | TYPE_TRAP), NULL)) && + (zdir > 0 ? other->position.z >= self->position.z : other->position.z <= self->position.z) && + (!other->animation->platform || !other->animation->platform[other->animpos][PLATFORM_HEIGHT])) + { + zdir = 0; + if ( self->falling ) hit |= 1; + if(te != other) //just in case they are the same obstacle + { + execute_onblocko_script(self, PLANE_Z, other); + } + } + + if ( hit && !self->hitwall && validanim(self, ANI_HITOBSTACLE) ) ent_set_anim(self, ANI_HITOBSTACLE, 0); + if ( hit && !self->hitwall ) self->hitwall = 1; + } + + if(!xdir && !zdir) + { + return 0; + } + x = self->position.x + xdir; + z = self->position.z + zdir; + + //-----------end of obstacle checking-------------- + + // ---------------- platform checking---------------- + + if(self->animation->size.y) + { + heightvar = self->animation->size.y; + } + else + { + heightvar = self->modeldata.size.y; + } + + // Check for obstacles with platform code and adjust base accordingly + if(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM) + { + int hit = 0; + + //if(xdir>0 ? other->position.x>self->position.x : other->position.xposition.x) {xdir = 0; } + //if(zdir>0 ? other->position.z>self->position.z : other->position.zposition.z) {zdir = 0; } + //temporary fix for thin platforms (i.e, offset is not between left and right side) + // TODO: find the collision position, merge with wall code + if(xdir && (other = check_platform_between(x, self->position.z, self->position.y, self->position.y + heightvar, self)) ) + { + xdir = 0; + if ( self->falling ) hit |= 1; + execute_onblockp_script(self, PLANE_X, other); + } + if(zdir && (other = check_platform_between(self->position.x, z, self->position.y, self->position.y + heightvar, self)) ) + { + zdir = 0; + if ( self->falling ) hit |= 1; + execute_onblockp_script(self, PLANE_Z, other); + } + + if ( hit && !self->hitwall && validanim(self, ANI_HITPLATFORM) ) ent_set_anim(self, ANI_HITPLATFORM, 0); + if ( hit && !self->hitwall ) self->hitwall = 1; + } + + if(!xdir && !zdir) + { + return 0; + } + x = self->position.x + xdir; + z = self->position.z + zdir; + + //-----------end of platform checking------------------ + + // ------------------ wall checking --------------------- + if(self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_WALL) + { + int hit = 0; + + if(xdir && (wall = checkwall_below(x, self->position.z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > self->position.y) + { + xdir = 0; + if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1; + execute_onblockw_script(self, &level->walls[wall], wall, PLANE_X); + } + if(zdir && (wall = checkwall_below(self->position.x, z, T_MAX_CHECK_ALTITUDE)) >= 0 && level->walls[wall].height > self->position.y) + { + zdir = 0; + if ( self->falling && (self->modeldata.hitwalltype < 0 || (self->modeldata.hitwalltype >= 0 && level->walls[wall].type == self->modeldata.hitwalltype)) ) hit |= 1; + execute_onblockw_script(self, &level->walls[wall], wall, PLANE_Z); + } + + if ( hit && !self->hitwall && validanim(self, ANI_HITWALL) ) ent_set_anim(self, ANI_HITWALL, 0); + if ( hit && !self->hitwall ) self->hitwall = 1; + } + + if(!xdir && !zdir) + { + return 0; + } + x = self->position.x + xdir; + z = self->position.z + zdir; + //----------------end of wall checking-------------- + + /* + // Final check to ensure we don't move into other obstacles. + // The old logic allows xdir if zdir is block and vice versa, + // but has a risk that the new destination is actually blocked + // because of multiple obstacle types. + // + // block old + // | / + // | / + // |/__ new + */ + //xdir = zdir = 0; + // TODO: should we add some checks in testmove to execute those onblockwhatever scripts? + t = testmove(self, self->position.x, self->position.z, x, z); + // extra hole check, only avoid hole while idling + if(t <= 0 && (t != -2 || needcheckhole)) + { + return 0; + } + + // do move and return + self->position.x += xdir; + self->position.z += zdir; + + if(xdir) + { + execute_onmovex_script(self); //X move event. + } + if(zdir) + { + execute_onmovez_script(self); //Z move event. + } + return 2 - (xdir == oxdir && zdir == ozdir); // return 2 for some checks +} + +// enemies run off after attack +void common_runoff() +{ + entity *target = normal_find_target(-1, 0); + + if(target == NULL) //sealth checking + { + self->velocity.z = self->velocity.x = 0; + self->takeaction = NULL; // OK, back to A.I. root + set_idle(self); + return; + } + + if(!(self->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP)) + { + self->direction = (self->position.x < target->position.x); + } + if(self->direction == DIRECTION_RIGHT) + { + self->velocity.x = -self->modeldata.speed.x / 2; + } + else + { + self->velocity.x = self->modeldata.speed.x / 2; + } + + self->velocity.z = 0; + + if(_time > self->stalltime) + { + self->takeaction = NULL; // OK, back to A.I. root + } + + adjust_walk_animation(target); +} + + +/*void common_stuck_underneath() +{ + int heightvar = self->animation->size.y ? self->animation->size.y : self->modeldata.size.y; + if(!check_platform_between(self->position.x, self->position.z, self->position.y, self->position.y + heightvar, self) ) + { + self->takeaction = NULL; + set_idle(self); + return; + } + if(player[self->playerindex].keys & FLAG_MOVELEFT) + { + self->direction = DIRECTION_LEFT; + } + else if(player[self->playerindex].keys & FLAG_MOVERIGHT) + { + self->direction = DIRECTION_RIGHT; + } + if(player[self->playerindex].playkeys & FLAG_ATTACK && validanim(self, ANI_DUCKATTACK)) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + self->takeaction = common_attack_proc; + set_attacking(self); + self->velocity.x = self->velocity.z = 0; + self->combostep[0] = 0; + self->running = RUN_STATE_NONE; + ent_set_anim(self, ANI_DUCKATTACK, 0); + return; + } + if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (player[self->playerindex].playkeys & FLAG_JUMP) && validanim(self, ANI_SLIDE)) + { + player[self->playerindex].playkeys &= ~FLAG_JUMP; + self->takeaction = common_attack_proc; + set_attacking(self); + self->velocity.x = self->velocity.z = 0; + self->combostep[0] = 0; + self->running = RUN_STATE_NONE; + ent_set_anim(self, ANI_SLIDE, 0); + return; + } +}*/ + + +// finish attacking, do something +void common_attack_finish() +{ + entity *target; + int stall; + + self->velocity.x = self->velocity.z = 0; + + if(self->modeldata.type & TYPE_PLAYER) + { + if (self->ducking & DUCK_ACTIVE) + { + doduck(self); + } + else + { + self->takeaction = NULL; + set_idle(self); + } + return; + } + + target = self->opponent; + + if(target && !(self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) && self->ducking == DUCK_NONE && diff(self->position.x, target->position.x) < 80 && (rand32() & 3)) + { + self->takeaction = NULL;//common_runoff; + self->destx = self->position.x > target->position.x ? MIN(self->position.x + 40, target->position.x + 80) : MAX(self->position.x - 40, target->position.x - 80); + self->destz = self->position.z; + self->velocity.x = self->position.x > target->position.x ? self->modeldata.speed.x : -self->modeldata.speed.x; + self->velocity.z = 0; + adjust_walk_animation(target); + self->idling = IDLING_PREPARED; + } + else + { + if (self->ducking & DUCK_ACTIVE) + { + doduck(self); + } + else + { + self->takeaction = NULL; + set_idle(self); + } + } + + stall = global_config.game_speed - self->modeldata.aggression; + if (stall < global_config.game_speed / 2) + { + stall = global_config.game_speed / 2; + } + self->stalltime = _time + MAX(0, stall); +} + +//while playing any simple animation +void common_animation_normal() +{ + if(self->animating) + { + return; + } + + self->takeaction = NULL; + set_idle(self); + + return; +} + +//while playing attack animation +void common_attack_proc() +{ + if(self->animating || diff(self->position.y, self->base) >= 4) + { + return; + } + + if(self->tocost) + { + + // Enemy was hit with a special so go ahead and subtract life + if(check_energy(ENERGY_TYPE_MP, self->animnum)) + { + self->energy_state.mp_current -= self->animation->energy_cost.cost; + } + else + { + self->energy_state.health_current -= self->animation->energy_cost.cost; + } + + self->tocost = 0; // Life is subtracted, so go ahead and reset the flag + } + + if(self == smartbomber) + { + // Player is done with the special animation, so unfreeze and execute a smart bomb + smart_bomb(self, self->modeldata.smartbomb); + smartbomber = NULL; + } + if(self->weapon_state & WEAPON_STATE_DEDUCT_USE) + { + subtract_shot(); + self->weapon_state &= ~WEAPON_STATE_DEDUCT_USE; + } + self->attacking = ATTACKING_NONE; + // end of attack proc + common_attack_finish(); +} + + +// dispatch A.I. attack +int common_attack() +{ + int aiattack; + + //if(stalker==self) return 0; + + if(_time / THINK_SPEED % 4 == 0) + { + return 0; + } + + if(self->modeldata.aiattack == -1) + { + return 0; + } + + aiattack = self->modeldata.aiattack & MASK_AIATTACK1; + + switch(aiattack) + { + case AIATTACK1_LONG: + case AIATTACK1_MELEE: + case AIATTACK1_NOATTACK: + return 0; + default: // this is the only available attack style by now + return inair(self) ? 0 : normal_attack(); + } +} + +//maybe used many times, so make a function +// A.I. characters will check if there's a wall infront, and jump onto it if possible +// return 1 if jump +int common_try_jump() +{ + const int COMMON_TRY_JUMP_DEFAULT = 1; + const int COMMON_TRY_JUMP_RUN = 2; + + float xdir = 0.0; + float zdir = 0.0; + int wall = 0; + int to_jump = 0; + float rmin = 0.0; + float rmax = 0.0; + float initial_z_velocity = 0.0; + + animation_id_t jump_animation = ANI_JUMP; + + /* + * If we can't jump at all, return false now. + */ + + if (self->modeldata.air_control & AIR_CONTROL_JUMP_DISABLE) + { + return 0; + } + + if(validanim(self, ANI_JUMP)) + { + xdir = 0; + wall = -1; + rmin = (float)self->modeldata.animation[ANI_JUMP]->range.x.min; + rmax = (float)self->modeldata.animation[ANI_JUMP]->range.x.max; + + if(self->direction == DIRECTION_RIGHT) + { + xdir = self->position.x + rmin; + } + else + { + xdir = self->position.x - rmin; + } + + /* + * If we can Z jump, apply velocity to ZDir. + * This means our checks below will have a + * bit of lateral (z) range. + */ + + if(self->modeldata.air_control & AIR_CONTROL_JUMP_Z_INITIAL) + { + zdir = self->position.z + self->velocity.z; + } + else + { + zdir = self->position.z; + } + + /* Check for obstruction in range of JUMP. */ + + if( (wall = checkwall_below(xdir, zdir, T_MAX_CHECK_ALTITUDE)) >= 0 && + level->walls[wall].height <= self->position.y + rmax && + !inair(self) && self->position.y < level->walls[wall].height ) + { + to_jump = COMMON_TRY_JUMP_DEFAULT; + } + else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) && + checkwall_index(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 && + check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL && + !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir)) + { + to_jump = COMMON_TRY_JUMP_DEFAULT; + } + } + + /* + * Caskey, Damon V. + * 2010-03-29 + * + * AI will check its RUNJUMP range if JUMP + * check failed. Code is pretty redundant, + * should probably move to a function later. + */ + + if(!to_jump && validanim(self, ANI_RUNJUMP)) + { + xdir = 0; + wall = -1; + rmin = (float)self->modeldata.animation[ANI_RUNJUMP]->range.x.min; + rmax = (float)self->modeldata.animation[ANI_RUNJUMP]->range.x.max; + + if(self->direction == DIRECTION_RIGHT) + { + xdir = self->position.x + rmin; + } + else + { + xdir = self->position.x - rmin; + } + + /* + * If we can Z jump, apply velocity to ZDir. + * This means our checks below will have a + * bit of lateral (z) range. + */ + + if(self->modeldata.air_control & AIR_CONTROL_JUMP_Z_INITIAL) + { + zdir = self->position.z + self->velocity.z; + } + else + { + zdir = self->position.z; + } + + /* Check for obstruction in range of RUNJUMP. */ + if( (wall = checkwall_below(xdir, zdir, T_MAX_CHECK_ALTITUDE)) >= 0 && + level->walls[wall].height <= self->position.y + rmax && + !inair(self) && self->position.y < level->walls[wall].height ) + { + to_jump = COMMON_TRY_JUMP_RUN; + } + else if(checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) && + checkwall_index(self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir) < 0 && + check_platform (self->position.x + (self->direction == DIRECTION_RIGHT ? 2 : -2), zdir, self) == NULL && + !checkhole(self->position.x + (self->direction == DIRECTION_RIGHT ? rmax : -rmax), zdir)) + { + to_jump = COMMON_TRY_JUMP_RUN; + } + } + + /* + * Now select the appropriate animation + * and send data to tryjump. + */ + + if(to_jump) + { + if(self->running || to_jump == COMMON_TRY_JUMP_RUN) + { + if(validanim(self, ANI_RUNJUMP)) + { + jump_animation = ANI_RUNJUMP; + } + else if(validanim(self, ANI_FORWARDJUMP)) + { + jump_animation = ANI_FORWARDJUMP; + } + else + { + jump_animation = ANI_JUMP; + } + } + else + { + if(validanim(self, ANI_FORWARDJUMP)) + { + jump_animation = ANI_FORWARDJUMP; + } + else + { + jump_animation = ANI_JUMP; + } + } + + if (self->modeldata.air_control & AIR_CONTROL_JUMP_Z_INITIAL) + { + initial_z_velocity = self->velocity.z; + } + else + { + initial_z_velocity = 0.0; + } + + tryjump(self->modeldata.runjumpheight, self->modeldata.jumpspeed* self->modeldata.runjumpdist, initial_z_velocity, jump_animation); + + return 1; + } + return 0; +} + +//test if direction is available for anim_up +static int testup(float xd, float zd) +{ + float f; + if(zd < 0) + { + if(!xd) + { + return 1; + } + f = zd / xd; + return (f > 0.5 || f < -0.5); + } + return 0; +} + +//test if direction is available for anim_down +static int testdown(float xd, float zd) +{ + float f; + if(zd > 0) + { + if(!xd) + { + return 1; + } + f = zd / xd; + return (f > 0.5 || f < -0.5); + } + return 0; +} + +void adjust_walk_animation(entity *other) +{ + int dir = 0; + if(self->running) + { + if (validanim(self, ANI_BACKRUN)) + { + if(is_in_backrun(self)) ent_set_anim(self, ANI_BACKRUN, 0); + else ent_set_anim(self, ANI_RUN, 0); + } + else ent_set_anim(self, ANI_RUN, 0); // Set to run animation if exists + return; + } + + //reset the walk animation + if(validanim(self, ANI_UP) && ((!other && testup(self->velocity.x, self->velocity.z)) || (other && testup(other->position.x - self->position.x, other->position.z - self->position.z)))) + { + common_up_anim(self); //ent_set_anim(self, ANI_UP, 0); + dir = 2; + } + else if(validanim(self, ANI_DOWN) && ((!other && testdown(self->velocity.x, self->velocity.z)) || (other && testdown(other->position.x - self->position.x, other->position.z - self->position.z)))) + { + common_down_anim(self); //ent_set_anim(self, ANI_DOWN, 0); + dir = 3; + } + else if((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && validanim(self, ANI_BACKWALK)) + { + common_backwalk_anim(self); //ent_set_anim(self, ANI_BACKWALK, 0); + } + else + { + common_walk_anim(self); //ent_set_anim(self, ANI_WALK, 0); + dir = 1; + } + + if(((self->direction == DIRECTION_RIGHT ? self->velocity.x < 0 : self->velocity.x > 0) && dir == 1) || + (dir == 2 && self->velocity.z > 0) || (dir == 3 && self->velocity.z < 0) ) + { + self->animating = ANIMATING_REVERSE; + } + else + { + self->animating = ANIMATING_FORWARD; + } +} + + +// ai character try to move towards the item +// TODO, check path or entity might get stuck under a wall +int common_try_pick(entity *other) +{ + // if there's an item to pick up, move towards it. + float maxspeed = self->modeldata.speed.x * 1.5; + float dx = diff(self->position.x, other->position.x); + float dz = diff(self->position.z, other->position.z); + + if(other == NULL || self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + + if(!dz && !dx) + { + self->velocity.x = self->velocity.z = 0; + self->destz = self->position.z; + self->destx = self->position.x; + } + else + { + self->velocity.x = maxspeed * dx / (dx + dz); + self->velocity.z = maxspeed * dz / (dx + dz); + self->destx = other->position.x; + self->destz = other->position.z; + } + if(self->position.x > other->position.x) + { + self->velocity.x = -self->velocity.x; + } + if(self->position.z > other->position.z) + { + self->velocity.z = -self->velocity.z; + } + + self->running = RUN_STATE_NONE; + + adjust_walk_animation(other); + + return 1; +} + +#define astarw 640 +#define astarh 360 +#define starts (astarw*astarh) + +// not so completed pathfinding logic based on a* +// it should be fairly slow due to the complicacy of terrain checking +// and it doesn't always work since walking from wall to wall +// requires jump. +int astar(entity *ent, float destx, float destz, float step, s_axis_plane_lateral_float **wp) +{ + int (*came_from)[astarw][astarh][2] = malloc(sizeof(*came_from)); + unsigned char (*closed)[astarw][astarh] = malloc(sizeof(*closed)); + int (*openset)[starts][2] = malloc(sizeof(*openset)); + float (*gscore)[astarw][astarh] = malloc(sizeof(*gscore)); + float (*hscore)[astarw][astarh] = malloc(sizeof(*hscore)); + float (*fscore)[astarw][astarh] = malloc(sizeof(*fscore)); + int opensize = 0; + int result = 0, mi = 0; + float tg, minf; + int x, z, i, j, tx, tz, better; + static int vx[8] = {0, 1, 1, 1, 0, -1, -1, -1}, vz[8] = { -1, -1, 0, 1, 1, 1, 0, -1}; + static float score[8] = {1.0, 1.4, 1.0, 1.4, 1.0, 1.4, 1.0, 1.4}; + + int sx = astarw / 2, sz = astarh / 2; + int dx = sx + (destx - ent->position.x) / step, dz = sz + (destz - ent->position.z) / step; + + *wp = NULL; + if(dx < 0 || dx >= astarw || dz < 0 || dz >= astarh) + { + goto pfclearup; + } + memset(closed, 0, sizeof(*closed)); + (*openset)[opensize][0] = sx; + (*openset)[opensize][1] = sz; + opensize++; + memset(came_from, 0, sizeof(*came_from)); + + (*gscore)[sx][sz] = 0; + (*hscore)[sx][sz] = diff(dx, sx) + diff(dz, sz); + (*fscore)[sx][sz] = (*gscore)[sx][sz] + (*hscore)[sx][sz]; + (*came_from)[sx][sz][0] = -1; + + while(opensize > 0) + { + minf = 9999999; + for(j = 0; j < opensize; j++) + { + x = (*openset)[j][0]; + z = (*openset)[j][1]; + if((*fscore)[x][z] < minf) + { + minf = (*fscore)[x][z]; + mi = j; + } + } + + x = (*openset)[mi][0]; + z = (*openset)[mi][1]; + if(x == dx && z == dz) + { + do + { + tx = (*came_from)[x][z][0]; + tz = (*came_from)[x][z][1]; + result++; + x = tx; + z = tz; + } + while(x >= 0); + *wp = malloc(sizeof(*wp) * result); + tx = (*came_from)[dx][dz][0]; + tz = (*came_from)[dx][dz][1]; + j = 0; + while(tx >= 0) + { + (*wp)[j].x = (tx - sx) * step + ent->position.x; + (*wp)[j].z = (tz - sz) * step + ent->position.z; + x = (*came_from)[tx][tz][0]; + z = (*came_from)[tx][tz][1]; + tx = x; + tz = z; + j++; + } + goto pfclearup; + } + + (*openset)[mi][0] = (*openset)[opensize - 1][0]; + (*openset)[mi][1] = (*openset)[opensize - 1][1]; + + opensize--; + (*closed)[x][z] = 1; + + for(i = 0; i < 8; i++) + { + tx = x + vx[i]; + tz = z + vz[i]; + + if(tx < 0 || tx >= astarw || tz < 0 || tz >= astarh) + { + continue; + } + if((*closed)[tx][tz]) + { + continue; + } + + if(!testmove(ent, (x - sx)*step + ent->position.x, (z - sz)*step + ent->position.z, (tx - sx)*step + ent->position.x, (tz - sz)*step + ent->position.z)) + { + // (*closed)[tx][tz] = 1; // don't add that to close list just in case the entity can jump + continue; + } + + tg = (*gscore)[x][z] + score[i]; + + for(j = 0; j < opensize; j++) + { + if((*openset)[j][0] == tx && (*openset)[j][1] == tz) + { + break; + } + } + + if(j == opensize) + { + (*openset)[opensize][0] = tx; + (*openset)[opensize][1] = tz; + opensize++; + better = 1; + } + else if (tg < (*gscore)[tx][tz]) + { + better = 1; + } + else + { + better = 0; + } + + if(better) + { + (*came_from)[tx][tz][0] = x; + (*came_from)[tx][tz][1] = z; + (*gscore)[tx][tz] = tg; + (*hscore)[tx][tz] = diff(tx, dx) + diff(tz, dz); + (*fscore)[tx][tz] = (*gscore)[tx][tz] + (*hscore)[tx][tz]; + } + } + } + +pfclearup: + if(came_from) + { + free(came_from); + } + came_from = NULL; + if(closed) + { + free(closed); + } + closed = NULL; + if(openset) + { + free(openset); + } + openset = NULL; + if(gscore) + { + free(gscore); + } + gscore = NULL; + if(hscore) + { + free(hscore); + } + hscore = NULL; + if(fscore) + { + free(fscore); + } + fscore = NULL; + + return result; +} + + +// use this after a wall checking meets +// wall sliding code +// whichside: +// 0 +// 1 3 +// 2 +int adjustdirection(float coords[], float offx, float offz, float ox, float oz, float xdir, float zdir, float *cxdir, float *czdir) +{ + float x[4], z[4]; + int whichside, i; + float a; + + for(i = 0; i < 4; i++) + { + x[i] = coords[2 + i] + coords[0] + offx; + } + z[1] = z[3] = coords[1] + offz; + z[0] = z[2] = z[1] - coords[6]; + + if(oz <= z[0]) + { + whichside = 0; + } + else if(oz >= z[1]) + { + whichside = 2; + } + else if(ox < x[2]) + { + whichside = 1; + } + else + { + whichside = 3; + } + + if(whichside == 0 || whichside == 2) + { + *cxdir = xdir; + *czdir = 0; + } + else + { + if((x[0] == x[1] && whichside == 1) || (x[2] == x[3] && whichside == 3)) + { + *cxdir = 0; + *czdir = zdir; + } + else + { + if(whichside == 1) + { + a = (z[1] - z[0]) / (x[1] - x[0]); + } + else + { + a = (z[3] - z[2]) / (x[3] - x[2]); + } + + *cxdir = xdir + zdir / a; + *czdir = a * xdir + zdir; + + a = (ABS(xdir) + ABS(zdir)) / (ABS(*cxdir) + ABS(*czdir)) ; + *cxdir *= a; + *czdir *= a; + } + } + //printf("%f, %f, %f, %f, %d\n", xdir, zdir, *cxdir, *czdir, whichside); + return whichside; +} + +// adjust walk speed for entity assuming it walks straight forward +// x, z - current position +// tx, tz - target position +// speed - max speed +// xdir, zdir - return values +void adjustspeed(float speed, float x, float z, float tx, float tz, float *xdir, float *zdir) +{ + float xd, zd; + float dx = diff(tx, x); + float dz = diff(tz, z) * 2; + + if(dx > dz) + { + xd = speed; + zd = xd / dx * dz; + } + else if(dz > dx) + { + zd = speed; + xd = zd / dz * dx; + } + else if(dx) + { + xd = zd = speed; + } + else + { + xd = zd = 0; + } + + if(tx < x) + { + xd = -xd; + } + if(tz < z) + { + zd = -zd; + } + + zd /= 2; + + *xdir = xd; + *zdir = zd; + +} + +int checkpathblocked() +{ + float x, z, r; + int aitype, wpc; + entity *target; + s_axis_plane_lateral_float *wp; + if(self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + if(self->stalltime >= _time) + { + aitype = self->modeldata.aimove; + if(self->modeldata.subtype == SUBTYPE_CHASE) + { + aitype |= AIMOVE1_CHASE; + } + + //be moo tolerable to PLAYER_MAX_Z and PLAYER_MIN_Z + if((self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MAX_Z && self->velocity.z > 0 && !self->velocity.x && self->velocity.z + self->position.z > PLAYER_MAX_Z) || + (self->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MIN_Z && self->velocity.z < 0 && !self->velocity.x && self->velocity.z + self->position.z < PLAYER_MIN_Z) ) + { + self->velocity.z = -self->velocity.z; + self->pathblocked = 0; + self->destz = self->velocity.z > 0 ? (PLAYER_MIN_Z + videomodes.vRes / 10) : (PLAYER_MAX_Z - videomodes.vRes / 10); + adjust_walk_animation(NULL); + return 1; + } + + if(self->pathblocked > 40 || (self->pathblocked > 20 && (aitype & (AIMOVE1_CHASEX | AIMOVE1_CHASEZ | AIMOVE1_CHASE)))) + { + if(self->modeldata.pathfindstep > 0) + { + target = normal_find_target(-1, 0); + + if(target) + { + //printf("pathfind: (%f %f)-(%f %f) %d steps\n", self->position.x, self->position.z, self->destx, self->destz, pathfind(self, self->destx, self->destz)); + if((wpc = astar(self, target->position.x, target->position.z, self->modeldata.pathfindstep, &wp)) > 0) + { + //printf("wp %d\n", wp); + self->numwaypoints = wpc; + if(self->waypoints) + { + free(self->waypoints); + } + self->waypoints = wp; + self->destx = self->waypoints[self->numwaypoints - 1].x; + self->destz = self->waypoints[self->numwaypoints - 1].z; + self->numwaypoints--; + self->pathblocked = 0; + return 1; + } + } + } + + x = self->velocity.x; + z = self->velocity.z; + if(x > 0) + { + x = self->modeldata.speed.x; + } + else if(x < 0) + { + x = -self->modeldata.speed.x; + } + if(z > 0) + { + z = self->modeldata.speed.x / 2; + } + else if(z < 0) + { + z = -self->modeldata.speed.x / 2; + } + r = randf(1); + if(r > 0.6f) + { + self->velocity.z = x; + self->velocity.x = -z; + } + else if(r > 0.2f) + { + self->velocity.z = -x; + self->velocity.x = z; + } + else + { + self->velocity.z = (1.0f - randf(2)) * self->modeldata.speed.x / 2; + self->velocity.x = (1.0f - randf(2)) * self->modeldata.speed.x; + } + self->running = RUN_STATE_NONE; // TODO: re-adjust walk speed + self->stalltime = _time + global_config.game_speed / 2; + adjust_walk_animation(NULL); + self->pathblocked = 0; + + if(self->velocity.z > 0) + { + self->destz = self->position.z + 40; + } + else if(self->velocity.z < 0) + { + self->destz = self->position.z - 40; + } + else + { + self->destz = self->position.z; + } + if(self->velocity.x > 0) + { + self->destx = self->position.x + 40; + } + else if(self->velocity.x < 0) + { + self->destx = self->position.x - 40; + } + else + { + self->destx = self->position.x; + } + + return 1; + + } + } + return 0; +} + + +// this is the most aggressive aimove pattern +// the entity will try get in and attack at anytime +// though the range depends on what attack you setup +int common_try_chase(entity* acting_entity, const entity *target, const bool axis_x, const bool axis_z) +{ + // start chasing the target + float dx, dz, range; + int randomatk; + + acting_entity->running = RUN_STATE_NONE; + + //adjustspeed(acting_entity->modeldata.speed.x, acting_entity->position.x, acting_entity->position.z, acting_entity->position.x + acting_entity->velocity.x, acting_entity->position.z + acting_entity->velocity.z, &acting_entity->velocity.x, &acting_entity->velocity.z); + + if(target == NULL || acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + + randomatk = pick_random_attack(NULL, 0); + + if(randomatk >= 0) + { + range = (acting_entity->modeldata.animation[randomatk]->range.x.min + acting_entity->modeldata.animation[randomatk]->range.x.max) / 2; + //printf("range picked: ani %d, range %f\n", randomatk, range); + if(range < 0) + { + range = acting_entity->modeldata.grabdistance; + } + else if(range > videomodes.hRes / 4) + { + range = videomodes.hRes / 4; + } + } + else + { + range = acting_entity->modeldata.grabdistance; + } + + if(axis_x) + { + if(acting_entity->position.x > target->position.x) + { + acting_entity->destx = target->position.x + range - 1; + } + else + { + acting_entity->destx = target->position.x - range + 1; + } + dx = diff(acting_entity->position.x, acting_entity->destx); + + if(dx > 150 && validanim(acting_entity, ANI_RUN)) + { + acting_entity->velocity.x = acting_entity->modeldata.runspeed; + acting_entity->running &= ~RUN_STATE_START_Z; + acting_entity->running |= RUN_STATE_START_X; + } + else + { + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + if(acting_entity->destx < acting_entity->position.x) + { + acting_entity->velocity.x = -acting_entity->velocity.x; + } + + //////////// + } + + if (axis_z) + { + acting_entity->destz = target->position.z; + dz = diff(acting_entity->position.z, acting_entity->destz); + + int going_up = (acting_entity->destz < acting_entity->position.z); + int run_flag_check = 0; + + if (going_up) + { + run_flag_check = + (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_ENABLED) && + (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_INITIAL); + } + else + { + run_flag_check = + (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_ENABLED) && + (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_INITIAL); + } + + if (dz > 100 && run_flag_check && validanim(acting_entity, ANI_RUN)) + { + acting_entity->velocity.z = acting_entity->modeldata.runspeed / 2; + acting_entity->running &= ~RUN_STATE_START_X; + acting_entity->running |= RUN_STATE_START_Z; + } + else + { + acting_entity->velocity.z = acting_entity->modeldata.speed.x / 2; + } + + if (going_up) + { + acting_entity->velocity.z = -acting_entity->velocity.z; + } + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2023-05-24 - Rewrite of orginal function by uTunnels, unknown date. +* +* Acting entity attempts to follow +* target entity allong allowed axis. +*/ +int common_try_follow(entity* const acting_entity, const entity *target, const bool axis_x, const bool axis_z) +{ + //printf("\n\n common_try_follow(%p, %p, %d, %d)", acting_entity, target, axis_x, axis_z); + + const s_child_follow* const child_follow = acting_entity->modeldata.child_follow; + + if(acting_entity == NULL || target == NULL || !child_follow || acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + + enum e_follow_state + { + FOLLOW_STATE_NONE = 0, + FOLLOW_STATE_RUN_X = (1 << 0), + FOLLOW_STATE_RUN_Z = (1 << 1), + FOLLOW_STATE_X = (1 << 2), + FOLLOW_STATE_Z = (1 << 3) + } follow_state = FOLLOW_STATE_NONE; + + float follow_speed = 0; + e_run_state run_state = RUN_STATE_NONE; + + /* + * Target positon, distance to target + * entity, and if acting entity is + * facing the target. + */ + + const s_axis_principal_float target_pos = { + .x = target->position.x + (target->direction == DIRECTION_RIGHT ? child_follow->follow_offset.x : -child_follow->follow_offset.x), + .y = target->position.y + child_follow->follow_offset.y, + .z = target->position.z + child_follow->follow_offset.z + }; + + const float distance_x = diff(acting_entity->position.x, target_pos.x); + const float distance_z = diff(acting_entity->position.z, target_pos.z); + + const int facing_target = (acting_entity->direction == DIRECTION_RIGHT ? acting_entity->position.x < target_pos.x : acting_entity->position.x > target_pos.x); + + /* + * Set up follow/run ranges. The use of + * idle range min value for min/max range + * defaults is for legacy compatability. + */ + + const int valid_idle = validanim(acting_entity, ANI_IDLE); + const int follow_range_default_x = (valid_idle) ? acting_entity->modeldata.animation[ANI_IDLE]->range.x.min : 100; + const int follow_range_default_z = (int)follow_range_default_x * 0.5; + const int follow_range_default_run = follow_range_default_x + 100; + + const s_range follow_range = { + .x = { + .max = (child_follow->follow_range.x.max == MAX_INT && valid_idle) ? follow_range_default_x : child_follow->follow_range.x.max, + .min = (child_follow->follow_range.x.min == MIN_INT && valid_idle) ? -follow_range_default_x : child_follow->follow_range.x.min + }, + .z = { + .max = (child_follow->follow_range.z.max == MAX_INT && valid_idle) ? follow_range_default_z : child_follow->follow_range.z.max, + .min = (child_follow->follow_range.z.min == MIN_INT && valid_idle) ? -follow_range_default_z : child_follow->follow_range.z.min + } + }; + + const s_range follow_run_range = { + .x = { + .max = child_follow->follow_run_range.x.max == MAX_INT ? follow_range_default_run : child_follow->follow_run_range.x.max, + .min = child_follow->follow_run_range.x.min == MIN_INT ? -follow_range_default_run : child_follow->follow_run_range.x.min + }, + + //.y = + // .max = child_follow->follow_run_range.y.max == MAX_INT ? (int)videomodes.vRes * 0.90 : child_follow->follow_run_range.y.max, + // .min = child_follow->follow_run_range.y.min == MIN_INT ? (int)videomodes.vRes * -0.90 : child_follow->follow_run_range.y.min + //}, + + .z = { + .max = child_follow->follow_run_range.z.max == MAX_INT ? follow_range_default_run : child_follow->follow_run_range.z.max, + .min = child_follow->follow_run_range.z.min == MIN_INT ? -follow_range_default_run : child_follow->follow_run_range.z.min + } + }; + + //printf("\n\t follow_range.x.min: %d", follow_range.x.min); + //printf("\n\t follow_range.x.max: %d", follow_range.x.max); + //printf("\n\t follow_range.z.min: %d", follow_range.z.min); + //printf("\n\t follow_range.z.max: %d", follow_range.z.max); + + + /* + * Let's try to follow on each axis. If ranges + * are not reversed, then we check to see if + * target is OUTSIDE the range, and if so, then + * set a flag to run downstream movement logic + * to pursue target. We do this for both walking + * and running. + */ + + if (axis_x) + { + if ((follow_range.x.min <= follow_range.x.max) + && !check_range_target_x(acting_entity, target, NULL, follow_range.x.min, follow_range.x.max)) { + + follow_state |= FOLLOW_STATE_X; + } + + /* Following and able to run? */ + if (follow_state & FOLLOW_STATE_X && validanim(acting_entity, ANI_RUN) + && facing_target + && (follow_run_range.x.min <= follow_run_range.x.max) + && !check_range_target_x(acting_entity, target, NULL, follow_run_range.x.min, follow_run_range.x.max)) { + + follow_state |= FOLLOW_STATE_RUN_X; + } + } + + if (axis_z) + { + if ((follow_range.z.min <= follow_range.z.max) + && !check_range_target_z(acting_entity, target, NULL, follow_range.z.min, follow_range.z.max)) { + + follow_state |= FOLLOW_STATE_Z; + } + + /* Following and able to run? */ + if (follow_state & FOLLOW_STATE_Z && validanim(acting_entity, ANI_RUN) + && facing_target + && (follow_run_range.z.min <= follow_run_range.z.max) + && !check_range_target_z(acting_entity, target, NULL, follow_run_range.z.min, follow_run_range.z.max)) { + + follow_state |= FOLLOW_STATE_RUN_Z; + } + } + + //printf("\n\n acting_entity: %p", acting_entity); + //printf("\n target_pos.x: %f", target_pos.x); + //printf("\n target_pos.z: %f", target_pos.z); + //printf("\n distance_x: %f", distance_x); + //printf("\n distance_max: %f", distance_max); + //printf("\n distance_x: %f", distance_x); + //printf("\n distance_z: %f", distance_z); + + //printf("\n\t follow_state: %d", follow_state); + //printf("\n\t axis_x: %d", axis_x); + //printf("\n\t axis_z: %d", axis_z); + + /* + * If we are farther from target on the + * X axis than the Z axis, we will try + * to initialize running toward target + * on X and veer toward target on Z. If + * we are farther on Z axis, then we'll + * do the opposite. + */ + + if (distance_x > distance_z) { + + if (follow_state & FOLLOW_STATE_X) { + + if (follow_state & FOLLOW_STATE_RUN_X) { + + if (acting_entity->direction == DIRECTION_LEFT + && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_X_LEFT_INITIAL | RUN_CONFIG_X_LEFT_ENABLED)) == (RUN_CONFIG_X_LEFT_INITIAL | RUN_CONFIG_X_LEFT_ENABLED)) { + follow_speed = -acting_entity->modeldata.runspeed; + run_state |= RUN_STATE_START_X; + } + else if (acting_entity->direction == DIRECTION_RIGHT + && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_X_RIGHT_INITIAL | RUN_CONFIG_X_RIGHT_ENABLED)) == (RUN_CONFIG_X_RIGHT_INITIAL | RUN_CONFIG_X_RIGHT_ENABLED)) { + follow_speed = acting_entity->modeldata.runspeed; + run_state |= RUN_STATE_START_X; + } + } + else { + follow_speed = acting_entity->modeldata.speed.x; + } + + /* Reverse speed if target is to our left. */ + if (acting_entity->position.x > target_pos.x) + { + follow_speed = -follow_speed; + } + + acting_entity->velocity.x = follow_speed; + acting_entity->running = run_state; + acting_entity->destx = target_pos.x; + } + + follow_speed = 0; + + if (follow_state & FOLLOW_STATE_Z) { + /* + * If we're running and have Z running enabled, let's + * veer ourselves toward the target. + */ + + if (follow_state & FOLLOW_STATE_RUN_Z) { + + if (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_ENABLED) { + follow_speed = acting_entity->modeldata.runspeed / 2; + } + else if (acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_ENABLED) { + follow_speed = -acting_entity->modeldata.runspeed / 2; + } + } + else { + follow_speed = acting_entity->modeldata.speed.x / 2; + } + + /* Reverse speed if target is behind our Z position. */ + if (acting_entity->position.z > target_pos.z) { + follow_speed = -follow_speed; + } + + acting_entity->velocity.z = follow_speed; + acting_entity->running = run_state; + acting_entity->destz = target_pos.z; + } + } + else { + + if (follow_state & FOLLOW_STATE_Z) + { + if (follow_state & FOLLOW_STATE_RUN_Z) + { + if (acting_entity->position.z < target_pos.z + && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_Z_DOWN_INITIAL | RUN_CONFIG_Z_DOWN_ENABLED)) == (RUN_CONFIG_Z_DOWN_INITIAL | RUN_CONFIG_Z_DOWN_ENABLED)) { + + follow_speed = acting_entity->modeldata.runspeed; + run_state = RUN_STATE_START_Z; + } + else if (acting_entity->position.z > target_pos.z + && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_Z_UP_INITIAL | RUN_CONFIG_Z_UP_ENABLED)) == (RUN_CONFIG_Z_UP_INITIAL | RUN_CONFIG_Z_UP_ENABLED)) { + + follow_speed = -acting_entity->modeldata.runspeed; + run_state = RUN_STATE_START_Z; + } + } + else { + follow_speed = acting_entity->modeldata.speed.x; + } + + acting_entity->velocity.z = follow_speed; + acting_entity->running = run_state; + acting_entity->destz = target_pos.z; + } + + follow_speed = 0; + + if (follow_state & FOLLOW_STATE_X) { + /* + * If we're running and have Z running enabled, let's + * veer ourselves toward the target. + */ + + if (follow_state & FOLLOW_STATE_RUN_X) { + + if (acting_entity->direction == DIRECTION_LEFT + && acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_LEFT_ENABLED) { + follow_speed = -acting_entity->modeldata.runspeed / 2; + } + else if (acting_entity->direction == DIRECTION_RIGHT + && acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_RIGHT_ENABLED) { + follow_speed = acting_entity->modeldata.runspeed / 2; + } + } + else { + follow_speed = acting_entity->modeldata.speed.x / 2; + } + + /* Reverse speed if target is behind our Z position. */ + if (acting_entity->position.z > target_pos.z) { + follow_speed = -follow_speed; + } + + acting_entity->velocity.z = follow_speed; + acting_entity->running = run_state; + acting_entity->destz = target_pos.z; + } + } + + //printf("\n\t acting_entity->running : %d", acting_entity->running); + //printf("\n\t acting_entity->destx: %f", acting_entity->destx); + //printf("\n\t acting_entity->destz: %f", acting_entity->destz); + //printf("\n\t acting_entity->velocity.x: %f", acting_entity->velocity.x); + //printf("\n\t acting_entity->velocity.z: %f", acting_entity->velocity.z); + + return 1; +} + +// try to avoid the target +// used by 'avoid avoidz avoidx +// Basic logic: the entity walk within a min distance and a max distance from the target +int common_try_avoid(entity *target, int dox, int doz) +{ + float dx, dz; + float maxdz, mindz, maxdx, mindx; + int randomatk; + + if(target == NULL || self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + + dx = diff(self->position.x, target->position.x); + dz = diff(self->position.z, target->position.z); + + randomatk = pick_random_attack(NULL, 0); + + if((rand32() & 15) < 8 && randomatk >= 0) + { + maxdx = self->modeldata.animation[randomatk]->range.x.max - self->modeldata.speed.x; + if(maxdx < videomodes.hRes / 5) + { + maxdx = videomodes.hRes / 5; + } + mindx = maxdx - 10; + maxdz = self->modeldata.animation[randomatk]->range.z.max - self->modeldata.speed.x; + if(maxdz < videomodes.vRes / 5) + { + maxdz = videomodes.vRes / 5; + } + mindz = maxdz - 10; + } + else + { + mindx = videomodes.hRes / 3; + maxdx = videomodes.hRes / 2; + mindz = videomodes.vRes / 3; + maxdz = videomodes.vRes / 2; + } + + if(dox) + { + if(self->position.x < screenx) + { + self->velocity.x = self->modeldata.speed.x; + self->destx = screenx + videomodes.hRes / 12.0; + } + else if(self->position.x > screenx + videomodes.hRes) + { + self->velocity.x = -self->modeldata.speed.x; + self->destx = screenx + videomodes.hRes * 11.0 / 12.0; + } + else if(dx < mindx) + { + self->velocity.x = (self->position.x < target->position.x) ? (-self->modeldata.speed.x) : self->modeldata.speed.x; + self->destx = (self->position.x < target->position.x) ? (target->position.x - maxdx) : (target->position.x + maxdx); + } + else if (dx > maxdx) + { + self->velocity.x = (self->position.x < target->position.x) ? self->modeldata.speed.x : (-self->modeldata.speed.x); + self->destx = (self->position.x < target->position.x) ? (target->position.x - mindx) : (target->position.x + mindx); + } + else + { + self->velocity.x = 0; + self->destx = self->position.x; + } + } + + if(doz) + { + if(self->position.z < screeny) + { + self->velocity.z = self->modeldata.speed.x / 2; + self->destz = screeny + videomodes.vRes / 12.0; + } + else if(self->position.z > screeny + videomodes.vRes) + { + self->velocity.z = -self->modeldata.speed.x / 2; + self->destz = screeny + videomodes.vRes * 11.0 / 12.0; + } + else if(dz < mindz) + { + self->velocity.z = (self->position.z < target->position.z) ? (-self->modeldata.speed.x / 2) : (self->modeldata.speed.x / 2); + self->destz = (self->position.z < target->position.z) ? (target->position.z - maxdz) : (target->position.z + maxdz); + } + else if(dz > maxdz) + { + self->velocity.z = (self->position.z < target->position.z) ? (self->modeldata.speed.x / 2) : (-self->modeldata.speed.x / 2); + self->destz = (self->position.z < target->position.z) ? (target->position.z - mindz) : (target->position.z + mindz); + } + else + { + self->velocity.z = 0; + self->destz = self->position.z; + } + } + + return 1; +} + +// wander completely and ignore the target +// this ai pattern only works when you use aimove wander, +// if you mix wander with other patterns like chase or avoid +// this pattern is not triggered +int common_try_wandercompletely(int dox, int doz) +{ + int rnum; + + if(self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + return 0; + } + + if(dox) + { + rnum = rand32() & 15; + if(rnum < 4) + { + self->velocity.x = -self->modeldata.speed.x; + } + else if(rnum > 11) + { + self->velocity.x = self->modeldata.speed.x; + } + else + { + self->velocity.x = 0; + } + if( self->position.x < screenx - 10) + { + self->velocity.x = self->modeldata.speed.x; + } + else if(self->position.x > screenx + videomodes.hRes + 10) + { + self->velocity.x = -self->modeldata.speed.x; + } + + if(self->velocity.x > 0) + { + self->destx = self->position.x + videomodes.hRes / 5; + } + else if(self->velocity.x < 0) + { + self->destx = self->position.x - videomodes.hRes / 5; + } + else + { + self->destx = self->position.x; + } + + } + if(doz) + { + rnum = rand32() & 15; + if(rnum < 4) + { + self->velocity.z = -self->modeldata.speed.x / 2; + } + else if(rnum > 11) + { + self->velocity.z = self->modeldata.speed.x / 2; + } + else + { + self->velocity.z = 0; + } + if(self->position.z < screeny - 10) + { + self->velocity.z = self->modeldata.speed.x / 2; + } + else if(self->position.z > screeny + videomodes.vRes + 10) + { + self->velocity.z = -self->modeldata.speed.x / 2; + } + + if(self->velocity.z > 0) + { + self->destz = self->position.z + videomodes.vRes / 5; + } + else if(self->velocity.z < 0) + { + self->destz = self->position.z - videomodes.vRes / 5; + } + else + { + self->destz = self->position.z; + } + + } + + return 1; + +} +/* +int assume_safe_distance(entity* target, int ani, int* minx, int* maxx, int* minz, int* maxz) +{ + int f, set = 0; + short tminx, tmaxx, tminz, tmaxz; + s_anim* ta; + short* coords; + if(validanim(target, ani)){ + ta = target->modeldata.animation[ani]; + *minx = *minz = 9999; + *maxx = *maxz = -9999; + if(ta->collision_attack){ + for(f=0; fnumframes; f++){ + if(!ta->collision_attack[f]) continue; + coords = ta->collision_attack[f]->coords; + if(target->direction == DIRECTION_RIGHT) { + tminx = coords[0]; + tmaxx = coords[2]; + }else{ + tminx = -coords[2]; + tmaxx = -coords[0]; + } + tminz = -coords[4]; + tmaxz = coords[4]; + if(tminx<*minx) + *minx = tminx; + if(tminz<*minz) + *minz = tminz; + if(tmaxx>*maxx) + *maxx = tmaxx; + if(tmaxz>*maxz) + *maxz = tmaxz; + + set = 1; + } + + if(set && self->animation->collision_body_legacy->coords && self->animation->collision_body_legacy[self->animpos]->coords){ + coords = self->animation->collision_body_legacy[self->animpos]->coords; + *minx -= coords[2] - coords[0]; + *minz -= coords[4]; + *maxx += coords[2] - coords[0]; + *maxz += coords[4]; + } + + return set; + } + } + + return 0; + +} +*/ +// for normal and default ai patttern +// the entity is not actually wandering +// they just go around the target and get close +// occasionally to attack +int common_try_wander(entity *target, int dox, int doz) +{ + int walk = 0, behind, grabd, mod; + + float diffx, diffz, //distance from target + returndx, returndz, //how far should keep from target + borderdx, borderdz, //how far should keep offscreen + mindx, mindz;// don't walk into the target + int rnum = rand32() & 15, t, randomatk; + + if(!target || (self->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE)) + { + return 0; + } + + diffx = diff(self->position.x, target->position.x); + diffz = diff(self->position.z, target->position.z); + behind = ((self->position.x < target->position.x) == target->direction); + grabd = self->modeldata.grabdistance; + //when entity is behind the target, it has a greater chance to go after the target + if(behind && diffx < grabd * 4 && diffz < grabd) //right behind, go for it + { + t = 13; + } + else if(behind) // only behind, half chance + { + t = 7; + } + else // otherwise, 1/8 chance + { + t = 2; + } + + if(behind && target->attacking != ATTACKING_NONE) + { + t += 5; + } + + // could use this to replace the completely wander ai + if(dox != doz) + { + t = 0; + } + + if(rnum < t) //chase target + { + returndx = videomodes.hRes / 4; + returndz = videomodes.vRes / 8; + } + else // only chase when too far away + { + returndx = videomodes.hRes * 0.6; + returndz = videomodes.vRes * 0.4; + } + if(rnum > 7) + { + borderdx = videomodes.hRes / 8; + borderdz = videomodes.vRes / 8; + } + else + { + borderdx = borderdz = 0; + } + + randomatk = pick_random_attack(NULL, 0); + + if(randomatk >= 0) + { + mindx = self->modeldata.animation[randomatk]->range.x.max - (self->modeldata.animation[randomatk]->range.x.max - self->modeldata.animation[randomatk]->range.x.min) / 4 - 1; + } + else + { + mindx = (!behind && target->attacking != ATTACKING_NONE) ? grabd * 3 : grabd * 1.2; + } + mindz = grabd / 4; + + mod = ((int)(_time / (videomodes.hRes / self->modeldata.speed.x)) + 1000 + self->energy_state.health_current / 3 + self->pathblocked + self->modeldata.aggression / 10) % 4; + if(mod < 0) + { + mod = -mod; + } + //if ((self->sortid / 100) % 2) + if (rand32() % 2) + { + mod = 3 - mod; + } + + if(dox) + { + if(self->position.x < screenx - borderdx) + { + self->velocity.x = self->modeldata.speed.x; + self->destx = screenx + videomodes.hRes / 8.0; + walk = 1; + } + else if (self->position.x > screenx + videomodes.hRes + borderdx) + { + self->velocity.x = -self->modeldata.speed.x; + self->destx = screenx + videomodes.hRes * 7.0 / 8.0; + walk = 1; + } + else if(diffx > returndx) + { + self->velocity.x = (self->position.x > target->position.x) ? -self->modeldata.speed.x : self->modeldata.speed.x; + self->destx = (self->position.x > target->position.x) ? (target->position.x + mindx) : (target->position.x - mindx); + walk = 1; + } + else + { + switch(mod) + { + case 0: + self->destx = target->position.x + grabd; + break; + case 2: + self->destx = target->position.x - grabd; + break; + case 1: + self->destx = target->position.x + videomodes.hRes * 0.4 + (self->energy_state.health_current / 3 % 20); + break; + case 3: + self->destx = target->position.x - videomodes.hRes * 0.4 - (self->energy_state.health_current / 3 % 20); + break; + } + self->velocity.x = self->position.x > self->destx ? -self->modeldata.speed.x : self->modeldata.speed.x; + walk = 1; + //printf("mod x %d\n", mod); + } + } + + if(doz) + { + if(self->position.z < screeny - borderdz) + { + self->velocity.z = self->modeldata.speed.x / 2; + self->destz = screeny + videomodes.vRes / 12.0; + walk |= 1; + } + else if (self->position.z > screeny + videomodes.vRes + borderdz) + { + self->velocity.z = -self->modeldata.speed.x / 2; + self->destz = screeny + videomodes.vRes * 11.0 / 12.0; + walk |= 1; + } + else if(diffz > returndz) + { + self->velocity.z = (self->position.z > target->position.z) ? -self->modeldata.speed.x / 2 : self->modeldata.speed.x / 2; + self->destz = (self->position.z > target->position.z) ? (target->position.z + mindz) : (target->position.z - mindz); + walk |= 1; + } + else + { + switch(mod) + { + case 1: + self->destz = target->position.z + grabd / 2; + break; + case 3: + self->destz = target->position.z - grabd / 2; + break; + case 2: + self->destz = target->position.z + MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 + (self->energy_state.health_current / 3 % 5); + break; + case 0: + self->destz = target->position.z - MIN((PLAYER_MAX_Z - PLAYER_MIN_Z), videomodes.vRes) * 0.25 - (self->energy_state.health_current / 3 % 5); + break; + } + self->velocity.z = self->position.z > self->destz ? -self->modeldata.speed.x / 2 : self->modeldata.speed.x / 2; + walk |= 1; + //printf("mod z %d\n", mod); + } + } + + return walk; +} + +// Caskey, Damon V. +// 208-04-09 +// +// Set up attack by item and execute didhit script as if item "hit" collector +// to allow easy item scripting. +void do_item_script(entity *ent, entity *item) +{ + s_attack attack = emptyattack; + attack.attack_type = ATK_ITEM; + + execute_didhit_script(item, ent, &attack, 0); +} + +//A.I chracter pickup an item +void common_pickupitem(entity *other) +{ + int pickup = 0; + //weapons + if(self->weapent == NULL && isSubtypeWeapon(other) && validanim(self, ANI_GET)) + { + self->takeaction = common_get; + dropweapon(self, 0); //don't bother dropping the previous one though, scine it won't pickup another + self->weapent = other; + + set_weapon(self, other->modeldata.weapon_properties.weapon_index, 0); + + set_getting(self); + self->velocity.x = self->velocity.z = 0; //stop moving + + /* Move to weapon location if it's an "animal". */ + if(self->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + self->direction = other->direction; + self->position.x = other->position.x; + self->position.z = other->position.z; + } + + if(!(other->nextanim & DELAY_FLAG_INFINITE)) + { + other->nextanim = animation_timestamp_add_bounded( + _time, + delay_multiply_bounded( + global_config.game_speed, + UINT64_C(999999) + ) + ); + } + + other->nextthink = _time + global_config.game_speed * 999999; + ent_set_anim(self, ANI_GET, 0); + pickup = 1; + } + // projectiles + else if(self->weapent == NULL && isSubtypeProjectile(other) && validanim(self, ANI_GET)) + { + self->takeaction = common_get; + dropweapon(self, 0); + self->weapent = other; + set_getting(self); + self->velocity.x = self->velocity.z = 0; //stop moving + if(!(other->nextanim & DELAY_FLAG_INFINITE)) { + other->nextanim = animation_timestamp_add_bounded( + _time, + delay_multiply_bounded( + global_config.game_speed, + UINT64_C(999999) + ) + ); + } + + + other->nextthink = _time + global_config.game_speed * 999999; + ent_set_anim(self, ANI_GET, 0); + pickup = 1; + } + // other items + else if(! isSubtypeWeapon(other) && ! isSubtypeProjectile(other) ) + { + if(validanim(self, ANI_GET) && !isSubtypeTouch(other)) + { + self->takeaction = common_get; + set_getting(self); + self->velocity.x = self->velocity.z = 0; //stop moving + ent_set_anim(self, ANI_GET, 0); + } + if(other->energy_state.health_current) + { + self->energy_state.health_current += other->energy_state.health_current; + if(self->energy_state.health_current > self->modeldata.health) + { + self->energy_state.health_current = self->modeldata.health; + } + other->energy_state.health_current = 0; + //if(global_sample_list.get >= 0) sound_play_sample(global_sample_list.get, 0, savedata.effectvol,savedata.effectvol, 100); + } + // else if, TODO: other effects + // kill that item + other->takeaction = suicide; + other->nextthink = _time + global_config.game_speed * 3; + pickup = 1; + } + // hide it + if(pickup) + { + do_item_script(self, other); + + other->position.z = ITEM_HIDE_POSITION_Z; + } +} + +// for old bikers +int biker_move() +{ + + if((self->direction == DIRECTION_RIGHT) ? (self->position.x > advancex + (videomodes.hRes + 200)) : (self->position.x < advancex - 200)) + { + self->direction = !self->direction; + self->attack_id_outgoing = 0; + self->position.z = (float)(PLAYER_MIN_Z + randf((float)(PLAYER_MAX_Z - PLAYER_MIN_Z))); + + if(global_sample_list.bike >= 0) + { + sound_play_sample(global_sample_list.bike, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(self->modeldata.speed.x) + { + self->velocity.x = (self->direction == DIRECTION_RIGHT) ? (self->modeldata.speed.x) : (-self->modeldata.speed.x); + } + else + { + self->velocity.x = (self->direction == DIRECTION_RIGHT) ? ((float)1.7 + randf((float)0.6)) : (-((float)1.7 + randf((float)0.6))); + } + } + + return 1; +} + +// for common arrow types +int arrow_move(entity* acting_entity) +{ + float dx; + float dz; + float maxspeed; + entity *target = NULL; + + // new subtype chase + if(acting_entity->modeldata.subtype == SUBTYPE_CHASE || acting_entity->modeldata.aimove & AIMOVE1_CHASE) + { + target = homing_find_target(acting_entity); + + if(target) + { + if(!(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP)) + { + acting_entity->direction = (target->position.x > acting_entity->position.x); + } + // start chasing the target + dx = diff(acting_entity->position.x, target->position.x); + dz = diff(acting_entity->position.z, target->position.z); + maxspeed = acting_entity->modeldata.speed.x * 1.5; + + if(!dz && !dx) + { + acting_entity->velocity.x = acting_entity->velocity.z = 0; + } + else + { + acting_entity->velocity.x = maxspeed * dx / (dx + dz); + acting_entity->velocity.z = maxspeed * dz / (dx + dz); + } + if(acting_entity->direction == DIRECTION_LEFT) + { + acting_entity->velocity.x = -acting_entity->velocity.x; + } + if(acting_entity->position.z > target->position.z) + { + acting_entity->velocity.z = -acting_entity->velocity.z; + } + } + else + { + if(!acting_entity->velocity.x && !acting_entity->velocity.z) + { + if(acting_entity->direction == DIRECTION_LEFT) + { + acting_entity->velocity.x = -acting_entity->modeldata.speed.x; + } + else if(acting_entity->direction == DIRECTION_RIGHT) + { + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + } + } + + if(!(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE)) + { + if(target && acting_entity->position.z > target->position.z && validanim(acting_entity, ANI_UP)) + { + common_up_anim(acting_entity); //ent_set_anim(self, ANI_UP, 0); + } + else if(target && target->position.z > acting_entity->position.z && validanim(acting_entity, ANI_DOWN)) + { + common_down_anim(acting_entity); //ent_set_anim(self, ANI_DOWN, 0); + } + else if(validanim(acting_entity, ANI_WALK)) + { + common_walk_anim(acting_entity); //ent_set_anim(self, ANI_WALK, 0); + } + else + { + if(validanim(acting_entity, ANI_IDLE)) ent_set_anim(acting_entity, ANI_IDLE, 0); + } + } + } + else + { + /* + * Apply speed to velocity. If spawned by legacy + * functions, only apply X axis for backward + * compatability. + */ + + if(acting_entity->direction == DIRECTION_LEFT) + { + acting_entity->velocity.x = -acting_entity->modeldata.speed.x; + } + else if(acting_entity->direction == DIRECTION_RIGHT) + { + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + + + if (!(acting_entity->projectile_prime & PROJECTILE_PRIME_INITIALIZE_LEGACY_PROJECTILE_FUNCTION)) + { + acting_entity->velocity.y = acting_entity->modeldata.speed.y; + acting_entity->velocity.z = acting_entity->modeldata.speed.z; + } + } + + if(level) + { + // Bounce off walls or platforms. + projectile_wall_deflect(acting_entity); + + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_PROJECTILE_BASE_DIE) + { + if (acting_entity->landed_on_platform || acting_entity->position.y <= acting_entity->base) + { + acting_entity->takeaction = common_fall; + acting_entity->attacking = ATTACKING_NONE; + acting_entity->energy_state.health_current = 0; + acting_entity->projectile = BLAST_NONE; + acting_entity->damage_on_landing.attack_force = 0; + acting_entity->damage_on_landing.attack_type = ATK_NONE; + + /* Use default attack values. */ + s_attack attack = emptyattack; + attack.attack_drop = 10000; + set_fall(acting_entity, acting_entity, &attack, 0); + } + } + } + + if(acting_entity->projectile_prime & PROJECTILE_PRIME_LAUNCH_STATIONARY)// PROJECTILE_PRIME_BASE_FLOOR) + { + acting_entity->autokill |= AUTOKILL_ANIMATION_COMPLETE; + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2022-06-28 +* +* Explode (play attack animation and stop) if detonate +* flag is set and entity meets conditions. +* +* Return 0 if conditions fail. Return 1 if conditions +* pass or detonation in progress. +*/ +int bomb_try_detonate(entity* acting_entity) +{ + //printf("\n %s:", "bomb_try_detonate"); + + /* + * If not already set to detonate, check if + * we are preared for detonation and in air. + */ + + //printf("\n\t %s: %d", "acting_entity->toexplode", acting_entity->toexplode); + + if (!(acting_entity->toexplode & (EXPLODE_DETONATE_HIT | EXPLODE_DETONATE_DAMAGED))) + { + if (!(acting_entity->toexplode & (EXPLODE_PREPARE_TOUCH | EXPLODE_PREPARE_GROUND))) + { + return 0; + } + + //printf("\n\t %s: %d", "inair(acting_entity)", inair(acting_entity)); + + /* + * - Touches a wall. + * - Touches a platform. + * - Touches base 0 and isn't in a hole. + */ + + //printf("\n\t %s: %d", "check_block_wall(acting_entity)", check_block_wall(acting_entity)); + + if (check_block_wall(acting_entity) == WALL_INDEX_NONE && + !check_block_obstacle(acting_entity) + && inair(acting_entity)) + { + return 0; + } + } + + //printf("\n\t %s: %p", "acting_entity->takeaction", acting_entity->takeaction); + //printf("\n\t %s: %p", "bomb_explode", bomb_explode); + //printf("\n\t %s: %f, %f, %f, %f", "Position (x,y,z,b):", acting_entity->position.x, acting_entity->position.y, acting_entity->position.z, acting_entity->base); + + /* Already detonated? */ + if (acting_entity->takeaction == bomb_explode) + { + return 1; + } + + /* + * Set explode action. Entity stays on screen + * until explode animation finishes, then kills + * itself. + */ + acting_entity->takeaction = bomb_explode; + + /* + * Stop movement. + */ + if (!checkhole(acting_entity->position.x, acting_entity->position.z)) + { + acting_entity->base = acting_entity->position.y; + acting_entity->velocity.y = 0; + acting_entity->velocity.x = 0; + acting_entity->velocity.z = 0; + } + + /* + * Play die sound if we have it. This can act as + * the explosion sound. + */ + if (acting_entity->modeldata.diesound >= 0) + { + sound_play_sample(acting_entity->modeldata.diesound, 0, savedata.effectvol, savedata.effectvol, 100); + } + + /* + * If we hit or got hit, then play ATTACK2. + * If we landed first, play ATTACK1. + */ + if (acting_entity->toexplode & (EXPLODE_DETONATE_HIT | EXPLODE_DETONATE_DAMAGED)) + { + /* + * Play explode animation if we have it. + * + * If we do not have explode animation and + * the explode was triggered by hitting, then + * let's behave like a typical projectile and + * check the remove flag. + */ + + if (validanim(acting_entity, ANI_ATTACK2)) + { + ent_set_anim(acting_entity, ANI_ATTACK2, 0); + //acting_entity->animation->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + else if(acting_entity->modeldata.remove_config & REMOVE_CONFIG_HIT && acting_entity->toexplode & EXPLODE_DETONATE_HIT) + { + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_BOMB_EXPLODE_ANIMATION_UNAVAILABLE); + } + } + else if (validanim(acting_entity, ANI_ATTACK1)) + { + ent_set_anim(acting_entity, ANI_ATTACK1, 0); + //acting_entity->animation->move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2019-12-22 +* +* Original author unknown. Refactored to stop using global self, +* and fix bomb falling after detonation. +*/ +int bomb_move(entity* acting_entity) +{ + /* + * If in air, and prepared to explode (meaning will detonate on contact), + * but NOT yet set to detonate, then we move using velocity (presumably + * we've been tossed and so any Z and Y momentum is already handled). + * + * EXPLODE_DETONATE_HIT status is applied by do_attack() if we touch + * ground or hit another entity. + * + * EXPLODE_DETONATE_DAMAGE set when hit by another entity attack. + * + * In either case case, we "explode" by playing an appropriate animation. + */ + + /* + * Bounce off walls or platforms if there + * is a fall animation. + */ + + if (projectile_wall_deflect(acting_entity)) + { + return 1; + } + + /* + * Check detonation conditions and + * apply accordingly. + */ + + if (bomb_try_detonate(acting_entity)) + { + return 1; + } + + if (acting_entity->direction == DIRECTION_LEFT) + { + acting_entity->velocity.x = -acting_entity->modeldata.speed.x; + } + else if (acting_entity->direction == DIRECTION_RIGHT) + { + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + + return 1; +} + +/* +* Caskey, Damon V. +* 2018-04-07 +* +* Find out if there is a wall blocking target entity, and +* if so return its array key. Returns WALL_INDEX_NONE if +* no blocking wall found. +*/ +int check_block_wall(entity *entity) +{ + //printf("\n\n check_block_wall(%p)", entity); + //printf("\n entity->modeldata.move_config_flags: %d", entity->modeldata.move_config_flags); + + int wall = WALL_INDEX_NONE; + + /* Target entity affected by walls? */ + if(entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_WALL) + { + //printf("\n\t Has constraint."); + + /* Get wall number at our Xand Z axis(if any). */ + wall = checkwall_index(entity->position.x, entity->position.z); + + //printf("\n\t wall: %d", wall); + + /* Did we find a wall? */ + if (wall >= 0) + { + /* + * Compare wall height to our current + * Y axis position. If the wall is + * higher, then it's blocking our way. + * We can return the wall number. + */ + + if(entity->position.y < level->walls[wall].height) + { + return wall; + } + } + } + + /* + * Got this far? Then there's no wall blocking our way. + */ + return WALL_INDEX_NONE; +} + +// Caskey, Damon V. +// 2018-04-07 +// +// Return obstacle entity that is blocking target +// entity (if any). Returns NULL if not blocking +// obstacle found. +entity *check_block_obstacle(entity *ent) +{ + entity *obstacle = NULL; + int height; + + // Target entity affected by obstacles? + if(ent->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM) + { + // Get the height. if entity does not have an + // animation height defined, then use its + // the entity height instead. + if(ent->animation->size.y) + { + height = ent->animation->size.y; + } + else + { + height = ent->modeldata.size.y; + } + + // Add Y position to get the exact Y axis + // height of the entity's top edge. + height += ent->position.y; + + // Find obstacle at entitiy's position (if any). + obstacle = check_platform_between(ent->position.x, ent->position.z, ent->position.y, height, ent); + } + + return obstacle; +} + +/* +* Caskey, Damon V +* 2018-04-06 +* +* Ricochet a projectile off of walls and platforms. +* Returns 1 on successful ricochet. 0 otherwise. +*/ +int projectile_wall_deflect(entity *acting_entity) +{ + const int RICHOCHET_FALL_FORCE = 10000; + const float RICHOCHET_VELOCITY_X_FACTOR = 0.25; // This value is multiplied by current velocity to get an X velocity value to bounce off wall.. + const float RICHOCHET_VELOCITY_Y = 2.5; // Base Y velocity applied when projectile bounces off wall. + const int RICHOCHET_VELOCITY_Y_RAND = 1; // Random seed for Y variance added to base Y velocity when bouncing off wall. + + float richochet_velocity_x; + s_attack attack = emptyattack; + + //printf("\n\n projectile_wall_deflect(%p)", acting_entity); + //printf("\n\t model(%s)", acting_entity->model->name); + //printf("\n\t acting_entity->drop: ", acting_entity->drop); + + if(validanim(acting_entity, ANI_FALL) && acting_entity->modeldata.move_config_flags & MOVE_CONFIG_PROJECTILE_WALL_BOUNCE) + { + //printf("\n\t Has Fall"); + //printf("\n\t position: %f, %f, %f", acting_entity->position.x, acting_entity->position.y, acting_entity->position.z); + //printf("\n\t velocity: %f, %f, %f", acting_entity->velocity.x, acting_entity->velocity.y, acting_entity->velocity.z); + + int blocking_wall; + entity *blocking_obstacle = NULL; + + /* + * Blocked by wall or obstacle? Note these + * functions return false if the entity + * is not subject to wall/obstacle. + */ + + blocking_wall = check_block_wall(acting_entity); + blocking_obstacle = check_block_obstacle(acting_entity); + + //printf("\n\t wall(%d), obstacle(%p)", blocking_wall, blocking_obstacle); + + if(blocking_wall >= 0 || blocking_obstacle) + { + //printf("\n\t Is blocked."); + + /* + * Use the projectile's speed and our factor to see + * how hard it will bounce off wall. + */ + richochet_velocity_x = -acting_entity->velocity.x * RICHOCHET_VELOCITY_X_FACTOR; + + //printf("\n\t richochet_velocity_x: %f", richochet_velocity_x); + + acting_entity->takeaction = common_fall; + acting_entity->attacking = ATTACKING_NONE; + acting_entity->energy_state.health_current = 0; + acting_entity->projectile = BLAST_NONE; + + acting_entity->velocity.x = richochet_velocity_x; + + acting_entity->velocity.z = 0.0; + acting_entity->damage_on_landing.attack_force = 0; + acting_entity->damage_on_landing.attack_type = ATK_NONE; + toss(acting_entity, RICHOCHET_VELOCITY_Y + randf(RICHOCHET_VELOCITY_Y_RAND)); + + /* Reset base detection */ + acting_entity->modeldata.move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL); + acting_entity->modeldata.move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + + acting_entity->base = 0; + + /* Use default attack values. */ + attack.attack_drop = RICHOCHET_FALL_FORCE; + set_fall(acting_entity, acting_entity, &attack, 0); + + //printf("\n\t velocity: %f, %f, %f", acting_entity->velocity.x, acting_entity->velocity.y, acting_entity->velocity.z); + + return 1; + } + + //printf("\n\t Not blocked."); + } + + /* Did not ricochet, so return false. */ + return 0; +} + +// Caskey, Damon V. +// 2018-04-06 +// +// Invert current sorting position vs. parent. +void sort_invert_by_parent(entity *ent, entity *parent) +{ + if(ent->sortid <= parent->sortid) + { + ent->sortid = parent->sortid + 1; + } + else + { + ent->sortid = parent->sortid - 1; + } +} + +int star_move() +{ + if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80) || (self->position.y <= self->base && (self->modeldata.death_config_flags & DEATH_CONFIG_MACRO_DEATH))) + { + kill_entity(self, KILL_ENTITY_TRIGGER_STAR_OUT_OF_BOUNDS); + return 0; + } + + //self->base -= 4; + //self->position.y = self->base; + + // Bounce off walls or platforms. + projectile_wall_deflect(self); + + if(self->landed_on_platform || self->position.y <= self->base) + { + self->takeaction = common_lie; + self->death_state |= DEATH_STATE_DEAD; + self->energy_state.health_current = 0; + if((self->modeldata.death_config_flags & (DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) == (DEATH_CONFIG_FALL_LAND_AIR | DEATH_CONFIG_FALL_LAND_GROUND | DEATH_CONFIG_REMOVE_VANISH_AIR | DEATH_CONFIG_REMOVE_VANISH_GROUND)) + { + self->animating = ANIMATING_NONE; + } + } + + return 1; +} + + +//dispatch move patterns +//root function for aimove +int common_move() +{ + entity* acting_entity = self; + e_aimove aimove; + int makestop = 0; + int reachx; + int reachz; + int air = inair(acting_entity); + entity *other = NULL; //item + entity *target = NULL;//hostile target + entity *owner = NULL; + entity *ent = NULL; + e_direction predir; + long stall; + int patx[5]; + int pxc; + int px; + int patz[5]; + int pzc; + int pz; + int fz; + + if(acting_entity->modeldata.aimove == -1) + { + return 0; // invalid value + } + + // get move pattern + aimove = acting_entity->modeldata.aimove & MASK_AIMOVE1; + +//if(stricmp(acting_entity->name, "os")==0) printf("%d\n", aimove); + // old and outdated patterns, but MUST be kept anyway + if(aimove & AIMOVE1_BIKER) + { + // for biker subtype + return biker_move(); + } + else if(aimove & AIMOVE1_ARROW) + { + // for common straight-flying arrow + return arrow_move(acting_entity); + } + else if(aimove & AIMOVE1_STAR) + { + // for a star, disappear when hit ground + return star_move(); + } + else if(aimove & AIMOVE1_BOMB) + { + // for a bomb, travel in a arc + return bomb_move(acting_entity); + } + else if(aimove & AIMOVE1_NOMOVE) + { + // no move, just return + return 0; + } + else + { + // all above are special move patterns, real AI goes here: + + // skip if the entity is in air, + // removing this and entity might be spawned walking in air + if (air) + { + return 0; + } + + // store this for turning checking + predir = acting_entity->direction; + + // find all possible entities for target + // bad for optimization, but makes better sense + if (acting_entity->custom_target == NULL || !acting_entity->custom_target->exists) target = normal_find_target(-1, 0); // confirm the target again + else target = acting_entity->custom_target; + + other = ((_time / global_config.game_speed + acting_entity->energy_state.health_current / 3 + 1000) % 15 < 10) ? normal_find_item() : NULL; // find an item + owner = acting_entity->parent; + + // temporary solution to turn off running if xdir is not set + // unless one day vertical running logic is written + if (!acting_entity->velocity.x) + { + acting_entity->running = RUN_STATE_NONE; + } + + // change direction unless the ai pattern ignores target or model has noflip + if (!(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_FLIP) && !acting_entity->running && aimove != AIMOVE1_WANDER) + { + if (other) //try to pick up an item, if possible + { + acting_entity->direction = (acting_entity->position.x < other->position.x) ? DIRECTION_RIGHT : DIRECTION_LEFT; + } + else if (target) + { + acting_entity->direction = (acting_entity->position.x < target->position.x) ? DIRECTION_RIGHT : DIRECTION_LEFT;; + } + else if (owner) + { + /* + * If we're following a parent, then check to see + * if we are in direction adjust range and if so + * we'll apply the direction adjustment. Otherwise + * we just face parent. + * + * Any range with min > max is true. + */ + + const s_child_follow* const child_follow = acting_entity->modeldata.child_follow; + + if (child_follow + && (child_follow->direction_adjust_range.x.min <= child_follow->direction_adjust_range.x.max && check_range_target_x(acting_entity, owner, NULL, child_follow->direction_adjust_range.x.min, child_follow->direction_adjust_range.x.max)) + && (child_follow->direction_adjust_range.z.min <= child_follow->direction_adjust_range.z.max && check_range_target_z(acting_entity, owner, NULL, child_follow->direction_adjust_range.z.min, child_follow->direction_adjust_range.z.max)) + && (child_follow->direction_adjust_range.y.min <= child_follow->direction_adjust_range.y.max && check_range_target_y(acting_entity, owner, NULL, child_follow->direction_adjust_range.y.min, child_follow->direction_adjust_range.y.max))) { + + acting_entity->direction = direction_get_adjustment_result(acting_entity, owner, child_follow->direction_adjust_config); + } + } + } + else if (aimove == AIMOVE1_WANDER) + { + if (acting_entity->velocity.x) + { + acting_entity->direction = (acting_entity->velocity.x > 0) ? DIRECTION_RIGHT : DIRECTION_LEFT; + } + } + + //turn back if we have a turn animation + // TODO, make a function for ai script + if (acting_entity->direction != predir && validanim(acting_entity, ANI_TURN) && acting_entity->ducking == DUCK_NONE) + { + acting_entity->takeaction = common_turn; + acting_entity->direction = (acting_entity->direction == DIRECTION_RIGHT) ? DIRECTION_LEFT : DIRECTION_RIGHT; + acting_entity->velocity.x = 0; + acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_TURN, 0); + return 1; + } + + //pick up the item if possible + if (other && diff(other->position.x, acting_entity->position.x) < (acting_entity->modeldata.grabdistance * 0.83333) + && diff(other->position.z, acting_entity->position.z) < (acting_entity->modeldata.grabdistance / 3) && + other->animation->vulnerable[other->animpos])//won't pickup an item that is not previous one + { + if (diff(acting_entity->base, other->position.y) < 0.1) + { + common_pickupitem(other); + return 1; + } + } + + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + acting_entity->idling = IDLING_PREPARED; + return 1; + } + + if (common_try_jump()) + { + acting_entity->numwaypoints = 0; + return 1; //need to jump? so quit + } + + if (checkpathblocked()) + { + return 1; // handle path blocked logic + } + + // judge next move if stalltime is expired + // skip if waypoints presents (passive move) + if (acting_entity->stalltime < _time && !acting_entity->waypoints) + { + if (other) + { + // try walking to the item + common_try_pick(other); + ent = other; + } + else + { + if (target && (acting_entity->modeldata.subtype == SUBTYPE_CHASE || + ((acting_entity->modeldata.type & TYPE_NPC) && acting_entity->parent))) + // try chase a target + { + aimove |= AIMOVE1_CHASE; + } + + if (aimove & AIMOVE1_CHASE) + { + aimove |= AIMOVE1_CHASEX | AIMOVE1_CHASEZ; + } + if (aimove & AIMOVE1_AVOID) + { + aimove |= AIMOVE1_AVOIDX | AIMOVE1_AVOIDZ; + } + + if (other != ent) + { + acting_entity->velocity.x = acting_entity->velocity.z = 0; + } + + if (!aimove && target) + { + common_try_wander(target, 1, 1); + ent = target; + } + else if (target && (!(acting_entity->modeldata.aimove & AIMOVE2_NOTARGETIDLE) || ((acting_entity->modeldata.aimove & AIMOVE2_NOTARGETIDLE) && target->animnum != ANI_IDLE))) + { + ent = target; + pxc = pzc = 0; + + if (aimove & AIMOVE1_WANDER) + { + patx[pxc++] = AIMOVE1_WANDER; + patx[pxc++] = AIMOVE1_WANDER; + patz[pzc++] = AIMOVE1_WANDER; + patz[pzc++] = AIMOVE1_WANDER; + } + if (aimove & AIMOVE1_CHASEX) + { + patx[pxc++] = AIMOVE1_CHASEX; + } + if (aimove & AIMOVE1_AVOIDX) + { + patx[pxc++] = AIMOVE1_AVOIDX; + } + if (aimove & AIMOVE1_CHASEZ) + { + patz[pzc++] = AIMOVE1_CHASEZ; + } + if (aimove & AIMOVE1_AVOIDZ) + { + patz[pzc++] = AIMOVE1_AVOIDZ; + } + if (!pxc) + { + patx[pxc++] = AIMOVE1_WANDER; + } + if (!pzc) + { + patz[pzc++] = AIMOVE1_WANDER; + } + px = patx[(rand32() & 0xff) % pxc]; + pz = patz[(rand32() & 0xff) % pzc]; + + fz = 0; + + aimove = (acting_entity->modeldata.aimove & MASK_AIMOVE1); + + //valid types: avoidx, aviodz, chasex, chasez, wander + if (px == AIMOVE1_WANDER) + { + if (pz == AIMOVE1_WANDER && aimove == AIMOVE1_WANDER) + { + common_try_wandercompletely(1, 1); + fz = 1; + } + else + { + common_try_wander(target, 1, 0); + } + + } + else if (px == AIMOVE1_CHASEX) + { + common_try_chase(acting_entity, target, 1, (pz == AIMOVE1_CHASEZ)); + fz = (pz == AIMOVE1_CHASEZ); + } + else if (px == AIMOVE1_AVOIDX) + { + common_try_avoid(target, 1, (pz == AIMOVE1_AVOIDZ)); + fz = (pz == AIMOVE1_AVOIDZ); + } + if (!fz) + { + if (pz == AIMOVE1_WANDER) + { + common_try_wander(target, 0, 1); + } + else if (pz == AIMOVE1_CHASEZ) + { + common_try_chase(acting_entity, target, 0, 1); + } + else if (pz == AIMOVE1_AVOIDZ) + { + common_try_avoid(target, 0, 1); + } + } + + } + else if (!common_try_follow(acting_entity, owner, 1, 1) && !(acting_entity->modeldata.aimove & AIMOVE2_NOTARGETIDLE)) + { + common_try_wandercompletely(1, 1); + ent = NULL; + } + else + { + ent = owner; + } + } + //end of if + + }//if(acting_entity->stalltime < _time ) + else + { + ent = other; + if (!ent) + { + ent = target; + } + if (!ent) + { + ent = owner; + } + } + if (acting_entity->numwaypoints == 0 && acting_entity->waypoints) + { + free(acting_entity->waypoints); + acting_entity->waypoints = NULL; + } + + //fix 2d level panic, or should this be moved to the very beginning? + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MIN_Z && acting_entity->destz < PLAYER_MIN_Z) + { + acting_entity->destz = PLAYER_MIN_Z; + } + if (acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_MAX_Z && acting_entity->destz > PLAYER_MAX_Z) + { + acting_entity->destz = PLAYER_MAX_Z; + } + + // don't run in passive move mode. The path could be complex and running may look bad. + if (acting_entity->waypoints) + { + acting_entity->running = RUN_STATE_NONE; + } + + if (acting_entity->direction == (acting_entity->destx < acting_entity->position.x)) + { + acting_entity->running = RUN_STATE_NONE; + } + + // make the entity walks in a straight path instead of flickering here and there + // acceleration can be added easily based on this logic, if necessary + adjustspeed(acting_entity->running ? acting_entity->modeldata.runspeed : acting_entity->modeldata.speed.x, + acting_entity->position.x, acting_entity->position.z, + acting_entity->destx, acting_entity->destz, + &acting_entity->velocity.x, + &acting_entity->velocity.z); + + /* + * If we can't run on a different axis + * then stay on the rails. + */ + + if (acting_entity->running & RUN_STATE_START_X) { + if ((acting_entity->velocity.z < 0 && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_ENABLED)) + || (acting_entity->velocity.z > 0 && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_ENABLED)) + || (acting_entity->velocity.x < 0 && acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_LEFT_DASH_FIXED) + || (acting_entity->velocity.x > 0 && acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_RIGHT_DASH_FIXED)) { + acting_entity->velocity.z = 0; + acting_entity->destz = acting_entity->position.z; + } + } + else if (acting_entity->running & RUN_STATE_START_Z) { + if ((acting_entity->velocity.x < 0 && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_LEFT_ENABLED)) + || (acting_entity->velocity.x > 0 && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_RIGHT_ENABLED)) + || (acting_entity->velocity.z < 0 && acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_DASH_FIXED) + || (acting_entity->velocity.z > 0 && acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_DASH_FIXED)) { + acting_entity->velocity.x = 0; + acting_entity->destz = acting_entity->position.x; + } + } + + // check destination point to make a stop or pop a waypoint from the stack + reachx = (diff(acting_entity->position.x, acting_entity->destx) < MAX(1, ABS(acting_entity->velocity.x))); + reachz = (diff(acting_entity->position.z, acting_entity->destz) < MAX(1, ABS(acting_entity->velocity.z))); + + // check destination point to make a stop or pop a waypoint from the stack + if(reachx && reachz) + { + if(acting_entity->waypoints && acting_entity->numwaypoints) + { + acting_entity->destx = acting_entity->waypoints[acting_entity->numwaypoints - 1].x; + acting_entity->destz = acting_entity->waypoints[acting_entity->numwaypoints - 1].z; + acting_entity->numwaypoints--; + } + else if(acting_entity->velocity.x || acting_entity->velocity.z) + { + makestop = 1; + } + } + + if(!acting_entity->waypoints || !acting_entity->numwaypoints) + { + if(reachx) + { + acting_entity->velocity.x = 0; + acting_entity->destx = acting_entity->position.x; + } + if(reachz) + { + acting_entity->velocity.z = 0; + acting_entity->destz = acting_entity->position.z; + } + } + + // IMPORTANT: stoped so play idle, preventinng funny stepping bug, but may cause flickering + if(!acting_entity->velocity.x && !acting_entity->velocity.z && !acting_entity->waypoints) + { + set_idle(acting_entity); + if(makestop) + { + stall = (global_config.game_speed - acting_entity->modeldata.aggression) / 2; + if(stall < global_config.game_speed / 5) + { + stall = global_config.game_speed / 5; + } + acting_entity->stalltime = _time + MAX(0, stall); + } + } + else + { + // readjust walk animation + adjust_walk_animation(ent); + // give proper stalltime if destination point is not reached + // if the destination point is not reachable, + // it should be already handled in checkpathblocked + if(_time > acting_entity->stalltime) + { + if(ABS(acting_entity->velocity.x) > ABS(acting_entity->velocity.z)) + { + stall = diff(acting_entity->destx, acting_entity->position.x) / ABS(acting_entity->velocity.x) * 2; + } + else if(acting_entity->velocity.z) + { + stall = diff(acting_entity->destz, acting_entity->position.z) / ABS(acting_entity->velocity.z) * 2; + } + else + { + stall = global_config.game_speed / 2; + } + acting_entity->stalltime = _time + MAX(0, stall); + } + } + + //target is moving? readjust destination sooner + if(aimove != AIMOVE1_WANDER && !acting_entity->waypoints && ent && (acting_entity->velocity.x || acting_entity->velocity.z) && (ent->velocity.x || ent->velocity.z)) + { + if(acting_entity->running && acting_entity->stalltime > _time + global_config.game_speed / 2) + { + acting_entity->stalltime = _time + global_config.game_speed / 2; + } + else if(!acting_entity->running && acting_entity->stalltime > _time + global_config.game_speed / 5) + { + acting_entity->stalltime = _time + global_config.game_speed / 5; + } + } + + return 1; + + } + + return 1; +} + + +void decide_stalker() +{ + entity *ent, *furthest = NULL; + int i; + int l = 0, r = 0; + float maxz = 0.0f, z; + + if(stalker && stalking) + { + return; + } + + firstplayer = NULL; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if(player[i].ent) + { + firstplayer = player[i].ent; + break; + } + } + + if(!firstplayer) + { + return; + } + + for(i = 0; i < ent_max; i++) + { + ent = ent_list[i]; + + if(ent->exists && !(ent->death_state & DEATH_STATE_DEAD) && (ent->modeldata.type & TYPE_ENEMY)) + { + if(ent->position.x > firstplayer->position.x) + { + r++; + } + else + { + l++; + } + + if((z = diff(ent->position.z, firstplayer->position.z)) >= maxz && + (ent->modeldata.aimove == 0 || (ent->modeldata.aimove & AIMOVE1_CHASE))) // 2 mostly used type + { + maxz = z; + furthest = ent; + } + } + } + + if((l > 1 && !r) || (r > 1 && !l)) + { + stalker = furthest; + //printf("** stalker decided: %s @ _time %d\n", stalker->name, _time); + } +} + + +void plan() +{ + decide_stalker(); +} + +void checkstalker() +{ + float maxspeed; + e_run_state running = RUN_STATE_NONE; + + if(self != stalker) + { + return; + } + + if(!firstplayer) + { + stalker = NULL; + return; + } + + if(stalking) + { + if(self->stalltime <= _time) + { + //printf("** stalk _time expired: %s @ _time %d\n", stalker->name, _time); + stalker = NULL; + } + return; + } + + if (validanim(self, ANI_RUN)) + { + running |= RUN_STATE_START_X; + } + + maxspeed = (running & RUN_STATE_START_X) ? self->modeldata.runspeed : self->modeldata.speed.x; + + self->velocity.x = maxspeed; + self->velocity.z = 0; + + if(self->position.x > firstplayer->position.x) + { + self->velocity.x = -self->velocity.x; + } + + self->running = running; + + + self->stalltime = _time + (diff(self->position.x, firstplayer->position.x) + 150) / maxspeed * THINK_SPEED; + + adjust_walk_animation(firstplayer); + + stalking = 1; + //printf("**start stalking: %s @ _time %d till @%d\n", stalker->name, time, self->stalltime); +} + +int checkplanned() +{ + return 0; +} + +/* +* Caskey, Damon V. +* 2023-05-24 (rewrite of orginal function by Utunnels, unknown date) +* +* Check if entity is outside of recall +* location, and if so, trigger the +* recall function. +*/ +int ai_check_recall() +{ + entity* const acting_entity = self; + const s_child_follow* const child_follow = acting_entity->modeldata.child_follow; + + if(!child_follow || acting_entity->link) + { + return 0; + } + + /* + * Set up recall range. X and Z using the + * idle range x max when supplied values + * are MIN/MAX is for legacy compatability. + */ + + const int valid_idle = validanim(acting_entity, ANI_IDLE); + const int default_range = (valid_idle) ? acting_entity->modeldata.animation[ANI_IDLE]->range.x.max : MAX_INT; + + const s_range recall_range = { + .x = { + .max = (child_follow->recall_range.x.max == MAX_INT && valid_idle) ? default_range : child_follow->recall_range.x.max, + .min = (child_follow->recall_range.x.min == MIN_INT && valid_idle) ? -default_range : child_follow->recall_range.x.min + }, + + .y = { + .max = child_follow->recall_range.y.max, + .min = child_follow->recall_range.y.min + }, + + .z = { + .max = (child_follow->recall_range.z.max == MAX_INT && valid_idle) ? default_range : child_follow->recall_range.z.max, + .min = (child_follow->recall_range.z.min == MIN_INT && valid_idle) ? -default_range : child_follow->recall_range.z.min + }, + + .base = { + .max = child_follow->recall_range.base.max, + .min = child_follow->recall_range.base.min + } + }; + + if(acting_entity->modeldata.subtype == SUBTYPE_FOLLOW && acting_entity->parent && + (!check_range_target_x(acting_entity, acting_entity->parent, NULL, recall_range.x.min, recall_range.x.max) || + !check_range_target_z(acting_entity, acting_entity->parent, NULL, recall_range.z.min, recall_range.z.max) || + !check_range_target_y(acting_entity, acting_entity->parent, NULL, recall_range.y.min, recall_range.y.max) || + !check_range_target_base(acting_entity, acting_entity->parent, NULL, recall_range.base.min, recall_range.base.max))) + { + acting_entity->takeaction = npc_recall; + return 1; + } + return 0; +} + +int ai_check_lie() +{ + if(self->drop + && self->position.y == self->base + && !self->velocity.y + && validanim(self, ANI_RISEATTACK) + && ((rand32() % (self->stalltime - _time + 1)) < 3) + && (self->energy_state.health_current > 0 + && _time > self->staydown.riseattack_stall)) + { + common_try_riseattack(); + return 1; + } + return 0; +} + +int ai_check_grabbed() +{ + if (!self->link) + { + return 0; + } + + if (self->grabbing) + { + return 0; + } + + if (self->inpain & ~IN_PAIN_NONE) + { + return 0; + } + + if (self->takeaction == common_prethrow) + { + return 0; + } + + if (inair(self)) + { + return 0; + } + + if (_time < self->stalltime) + { + return 0; + } + + if (!validanim(self, ANI_SPECIAL)) + { + return 0; + } + + check_special(); + + return 1; +} + +int ai_check_grab() +{ + if(self->grabbing && self->attacking == ATTACKING_NONE) + { + common_grab_check(); + return 1; + } + return 0; +} + +int ai_check_escape() +{ + if((self->escapecount > self->modeldata.escapehits) && !inair(self) && validanim(self, ANI_SPECIAL2)) + { + // Counter the player! + check_costmove(ANI_SPECIAL2, 0, 0); + return 1; + } + return 0; +} + +int ai_check_busy() +{ + return self->link || !self->idling; +} + +int ai_check_ducking() +{ + int indirect; + entity *target = NULL; + float t_rangex = 60.0f; + float t_rangez = 30.0f; + + if ((self->ducking & DUCK_PREPARED) || (self->ducking & DUCK_RISE)) + { + return 1; + } + + if(self->link || inair(self)) + { + return 0; + } + + if( !validanim(self, ANI_DUCK) || !validanim(self, ANI_DUCKATTACK) ) + { + return 0; + } + + if(self->projectile & BLAST_ATTACK) + { + indirect = 1; + } + + if(self->custom_target == NULL || !self->custom_target->exists ) target = normal_find_target(-1, 0); + else target = self->custom_target; + + if(target && !(faction_check_can_damage(self, target, indirect))) + { + return 0; + } + + if ((self->ducking & DUCK_ACTIVE) && self->animnum == ANI_DUCK) + { + if ( !target || inair(target) || !(target->ducking & DUCK_ACTIVE) || + (!check_range_target_all(self,target,ANI_DUCK, 0, 0) && + diff(self->position.x, target->position.x) > t_rangex && diff(self->position.z, target->position.z) > t_rangez) ) + { + self->velocity.x = self->velocity.z = 0; + tryduckrise(self); + return 1; + } + } + + if(!target || !(target->ducking & DUCK_ACTIVE)) + { + return 0; + } + else + { + if (self->ducking & DUCK_ACTIVE) + { + return 1; + } + else + { + int range_flag = check_range_target_all(self,target,ANI_DUCK, 0, 0); + if (!range_flag) + { + if ( diff(self->position.x, target->position.x) <= t_rangex && + diff(self->position.z, target->position.z) <= t_rangez) range_flag = 1; + } + if (!range_flag) return 0; + + if(self->ducking == DUCK_NONE) + { + tryduck(self); + } + return 1; + } + } + + return 0; +} + + +// A.I root +void common_think() +{ + + if(self->death_state & DEATH_STATE_DEAD) + { + return; + } + + //if(checkplanned()) return; + + // too far away , do a warp + if(ai_check_recall()) + { + return; + } + + // rise? try rise attack + if(ai_check_lie()) + { + return; + } + + // Escape? + if(ai_check_grabbed()) + { + return; + } + + //grabbing something + if(ai_check_grab()) + { + return; + } + + // Enemies can now escape non-knockdown spammage (What a weird phrase)! + if(ai_check_escape()) + { + return; + } + + // busy right now? + if(ai_check_busy()) + { + return; + } + + // idle, so try to attack or judge next move + // dont move if fall into a hole or off a wall + if(common_attack()) + { + return; + } + + // target is ducking? try to ducking.. + if(ai_check_ducking()) + { + return; + } + + common_move(); +} + +////////////////////////////////////////////////////////////////////////// + +void suicide() +{ + + if(_time < self->stalltime) + { + return; + } + level_completed |= self->boss; + level_completed_defeating_boss |= self->boss; + + kill_entity(self, KILL_ENTITY_TRIGGER_SUICIDE); +} + + + +// Re-enter playfield +// Used by player_fall and player_takedamage +void player_die_entity(entity* acting_entity) +{ + int playerindex = acting_entity->playerindex; + int i = 0; + + if(!(global_config.cheats & CHEAT_OPTIONS_LIVES_ACTIVE)) + { + --player[playerindex].lives; + } + + if(firstplayer == acting_entity) + { + firstplayer = NULL; + } + + execute_pdie_script(playerindex); + + if(nomaxrushreset[4] >= 1) + { + nomaxrushreset[playerindex] = player[playerindex].ent->rush.max; + } + player[playerindex].ent = NULL; + player[playerindex].spawnhealth = acting_entity->modeldata.health; + player[playerindex].spawnmp = acting_entity->modeldata.mp; + + /* + * Handle the body. If any corpse flags + * are set, we leave entity on screen + * and make it inert. Otherwise we can + * just run kill function to remove. + * + * REMOVE_CORPSE_* flags refer to how + * the live entity is removed from the + * game while leaving a corpse behind. + */ + + static const e_death_config_flags leave_corpse = DEATH_CONFIG_REMOVE_CORPSE_AIR | DEATH_CONFIG_REMOVE_CORPSE_GROUND; + + if (acting_entity->modeldata.death_config_flags & leave_corpse) { + acting_entity->think = NULL; + acting_entity->takeaction = NULL; + acting_entity->death_state |= DEATH_STATE_CORPSE; + } else { + kill_entity(acting_entity, KILL_ENTITY_TRIGGER_PLAYER_DEATH); + } + + if(player[playerindex].lives <= 0) + { + int all_p_ko = 0; + + // Count number of KO'd (dead) players, by looping player + // indexes and incrementing when player does not have + // an entity. + for(i = 0; i < MAX_PLAYERS; i++) + { + if (!player[i].ent) + { + ++all_p_ko; + } + } + + // If all players are KO'd, then KO count = 1. + // Otherwise, set it to 0. + all_p_ko = (all_p_ko >= MAX_PLAYERS) ? 1 : 0; + + // All players KO'd? + if(all_p_ko) + { + int all_p_nojoin = 0; + int all_p_nocredits = 0; + + // All players NOT joining? + // Same logic as all player KO. + for(i = 0; i < MAX_PLAYERS; i++) + { + if (!player[i].joining) + { + ++all_p_nojoin; + } + } + + all_p_nojoin = (all_p_nojoin >= MAX_PLAYERS) ? 1 : 0; + + // All players out of credits? + // Same logic as all player KO. + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].credits < 1) + { + ++all_p_nocredits; + } + } + + all_p_nocredits = (all_p_nocredits >= MAX_PLAYERS) ? 1 : 0; + + // Set the timer to a 10 second count down. + timeleft = 10 * global_config.counter_speed; + + // No one joining in? + if(all_p_nojoin) + { + // If the player can't continue, let's set the time over + // to end almost instantly so they won't have to wait. + + // If noshare is enabled, credit shares are not allowed. Verify all + // player individual credit supplies are empty. Otherwise credit + // shares are allowed, so verify pool of credits is empty. + if (noshare) + { + if (all_p_nocredits) + { + timeleft = global_config.counter_speed / 2; + } + } + else + { + if (credits < 1) + { + timeleft = global_config.counter_speed / 2; + } + } + } + } + + if(acting_entity->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_STAGE) + { + player[playerindex].weapnum = level->setweap; + } + + if(nomaxrushreset[4] != 2) + { + nomaxrushreset[playerindex] = 0; + } + + return; + } + else + { + spawnplayer(playerindex); + execute_respawn_script(playerindex); + if(!nodropen) + { + if (savedata.joyrumble[playerindex]) control_rumble(playerindex, 1, 125); + drop_all_enemies(); + } + } + + if(!level->noreset) + { + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + } + +} + +void player_die(void) +{ + player_die_entity(self); +} + + + +int player_trymove(float xdir, float zdir) +{ + return common_trymove(xdir, zdir); +} + +int check_energy(e_cost_check which, int ani) +{ + int result = FALSE; + e_entity_type type; //Entity type. + s_energy_cost energy_cost; + + // Get animation's energy_cost data. + energy_cost.cost = self->modeldata.animation[ani]->energy_cost.cost; + energy_cost.disable = self->modeldata.animation[ani]->energy_cost.disable; + energy_cost.mponly = self->modeldata.animation[ani]->energy_cost.mponly; + + // Get entity type. + type = self->modeldata.type; + + // If we're bind and special is overridden, then + // return false. + if(type & (TYPE_ENEMY | TYPE_NPC)) + { + if(check_bind_override(self, BIND_CONFIG_OVERRIDE_SPECIAL_AI)) + { + return FALSE; + } + } + else if(type & TYPE_PLAYER) + { + if(check_bind_override(self, BIND_CONFIG_OVERRIDE_SPECIAL_PLAYER)) + { + return FALSE; + } + } + + if(validanim(self, ani)) + { + // Caskey, Damon V. + // 2010-05-08 + // + // It is now possible to individually disable specials. In + // many cases (weapons in particular) this can help cut down the need for + // superfluous models when differing abilities are desired for players, + // enemies, or npcs. + // Kratus (10-2021) Fixed the new broken code for "disable" flag check, back to the previous code + if(!(energy_cost.disable == type // Disabled by type? + || (energy_cost.disable == -1) // Disabled for all? + || (energy_cost.disable == -2 && (type & (TYPE_ENEMY | TYPE_NPC))) // Disabled for all AI? + || (energy_cost.disable == -3 && (type & (TYPE_PLAYER | TYPE_NPC))) // Disabled for players & NPCs? + || (energy_cost.disable == -4 && (type & (TYPE_PLAYER | TYPE_ENEMY))))) // Disabled for all AI? + { + // No seal or seal is less/same as energy cost? + if (!self->seal || self->seal >= energy_cost.cost) + { + if((which == ENERGY_TYPE_MP && (energy_cost.mponly != COST_TYPE_HP_ONLY) && (self->energy_state.mp_current >= energy_cost.cost)) + || (which == ENERGY_TYPE_HP && (energy_cost.mponly != COST_TYPE_MP_ONLY) && (self->energy_state.health_current > energy_cost.cost))) + { + result = TRUE; + } + else + { + //DC 2009-01-23 + //Tried putting the CANT animation here to keep code compacted, but won't work. I'll come back to this. + //if (validanim(self,ani)){ + // ent_set_anim(self, ANI_CANT, 0); + // self->takeaction = common_attack_proc; + // player[self->playerindex].playkeys = 0; + //} + } + } + } + } + + return result; +} + +// Caskey Damon V. +// 2018-05-10 +// +// Replaces unreadable check_range() macro. Runs individual +// check range functions for each axis and returns true +// if target is within range of ALL. +bool check_range_target_all(const entity *ent, const entity *target, const animation_id_t animation_id, int64_t range_min, int64_t range_max) +{ + // Must have a valid target entity. + if(!target) + { + return false; + } + + // Get pointer to animation. + const s_anim *animation = ent->modeldata.animation[animation_id]; + + if (!check_range_target_x(ent, target, animation, range_min, range_max)) + { + return false; + } + + if (!check_range_target_y(ent, target, animation, range_min, range_max)) + { + return false; + } + + if (!check_range_target_z(ent, target, animation, range_min, range_max)) + { + return false; + } + + if (!check_range_target_base(ent, target, animation, range_min, range_max)) + { + return false; + } + + return true; +} + +// Caskey, Damon V. +// 2018-05-12 +// +// Return true if target is within Base range +// of entity's animation. +bool check_range_target_base(const entity *acting_entity, const entity *target, const s_anim *animation, int64_t range_min, int64_t range_max) +{ + int ent_base; + int target_base; + + s_metric_range range; + + // Must have a target. + if(!acting_entity || !target) + { + return false; + } + + range.max = range_max; + range.min = range_min; + + if (animation) + { + range.max += animation->range.base.max; + range.min += animation->range.base.min; + } + + // Get positions cast as integers. + ent_base = (int)acting_entity->base; + target_base = (int)target->base; + + // Subtract entity Base position from target position. + target_base -= ent_base; + + // Return true if final target location is + // within range min and max. + return (target_base >= range.min + && target_base <= range.max) ? true : false; +} + +// Caskey, Damon V. +// 2018-05-10 +// +// Return true if target is within X range +// of entity's animation. +bool check_range_target_x(const entity *acting_entity, const entity *target, const s_anim *animation, int64_t range_min, int64_t range_max) +{ + // Must have entities. + if(!acting_entity || !target) + { + return false; + } + + // Get positions cast as integers. + const int ent_x = (int)acting_entity->position.x; + const int target_x = (int)target->position.x; + + s_metric_range range; + + range.max = range_max; + range.min = range_min; + + if (animation) + { + range.max += animation->range.x.max; + range.min += animation->range.x.min; + } + + // Return true if final target location is + // within range X min and max. Range comparison + // is reversed when entity faces left. + if(acting_entity->direction == DIRECTION_RIGHT) + { + // Add range to entity X position + // for final X range coordinates. + range.min = ent_x + range.min; + range.max = ent_x + range.max; + + return (target_x >= range.min + && target_x <= range.max) ? true : false; + } + else + { + // Subtract range from entity X + // position for final X range coordinates. + range.min = ent_x - range.min; + range.max = ent_x - range.max; + + return (target_x <= range.min + && target_x >= range.max) ? true : false; + } +} + +// Caskey, Damon V. +// 2018-05-10 +// +// Return true if target is within Y range +// of entity's animation. +bool check_range_target_y(const entity *acting_entity, const entity *target, const s_anim *animation, int64_t range_min, int64_t range_max) +{ + // Must have a target. + if(!acting_entity || !target) + { + return false; + } + + s_metric_range range; + + range.max = range_max; + range.min = range_min; + + if (animation) + { + range.max += animation->range.y.max; + range.min += animation->range.y.min; + } + + // Get positions cast as integers. + const int ent_y = (int)acting_entity->position.y; + const int target_y = (int)target->position.y - ent_y; + + // Return true if final target location is + // within range min and max. + return (target_y >= range.min + && target_y <= range.max) ? true : false; +} + +// Caskey, Damon V. +// 2018-05-12 +// +// Return true if target is within Z range +// of entity's animation. +bool check_range_target_z(const entity * acting_entity, const entity *target, const s_anim *animation, int64_t range_min, int64_t range_max) +{ + // Must have a target. + if(!acting_entity || !target) + { + return false; + } + + s_metric_range range; + + range.min = range_min; + range.max = range_max; + + if (animation) + { + range.max += animation->range.z.max; + range.min += animation->range.z.min; + } + + // Get positions cast as integers. + const int ent_z = (int)acting_entity->position.z; + const int target_z = (int)target->position.z - ent_z; + + // Return true if final target location is + // within range min and max. + return (target_z >= range.min + && target_z <= range.max) ? true : false; +} + +int check_special() +{ + entity *e; + if((!level->nospecial || level->nospecial == 3) && validanim(self, ANI_SPECIAL) && + (check_energy(ENERGY_TYPE_HP, ANI_SPECIAL) || check_energy(ENERGY_TYPE_MP, ANI_SPECIAL)) + ) + { + self->takeaction = common_attack_proc; + set_attacking(self); + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + + e = self->link; + if(e) + { + e->takeaction = NULL; + ent_unlink(self); + set_idle(e); + } + + if(self->modeldata.smartbomb && !self->modeldata.dofreeze) + { + smart_bomb(self, self->modeldata.smartbomb); // do smartbomb immediately if it doesn't freeze screen + } + + self->running = RUN_STATE_NONE; // If special is executed while running, ceases to run + self->velocity.x = self->velocity.z = 0; + ent_set_anim(self, ANI_SPECIAL, 0); + + if(self->modeldata.dofreeze) + { + smartbomber = self; // Freezes the animations of all enemies/players while special is executed + } + + // Kratus (10-2021) Now the "infinite health cheat" affects players only, not enemies or npc + // And now the "infinite health cheat" will only work when the cost is HEALTH, will not work when the cost is MP anymore + if(self->modeldata.type & TYPE_PLAYER) + { + if(!nocost) + { + if(!(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE)) + { + if(self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, ANI_SPECIAL)) + { + self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + else + { + self->energy_state.health_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + } + } + else + { + if(self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, ANI_SPECIAL)) + { + self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + } + } + } + } + else + { + if(!nocost) + { + if(self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, ANI_SPECIAL)) + { + self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + else + { + self->energy_state.health_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + } + } + } + return 1; + } + return 0; +} + +// Check keys for special move. Used several times, so I func'd it. +// Kratus (10-2021) Added new flags to use with another ATTACK# keys as an new alternative +int player_check_special() +{ + key_mask_t thekey = 0; + key_mask_t player_keys = player[self->playerindex].playkeys; + + switch (global_config.ajspecial) + { + case AJSPECIAL_KEY_SPECIAL: + + if (player_keys & FLAG_SPECIAL) + { + thekey = FLAG_SPECIAL; + } + + break; + + case AJSPECIAL_KEY_DOUBLE: + + if (!validanim(self, ANI_BLOCK) && player_keys & FLAG_SPECIAL) + { + thekey = FLAG_SPECIAL; + } + else if (player_keys & FLAG_JUMP && player_keys & FLAG_ATTACK) + { + thekey = FLAG_JUMP | FLAG_ATTACK; + } + break; + + case AJSPECIAL_KEY_ATTACK2: + + if (player_keys & FLAG_ATTACK2) + { + thekey = FLAG_ATTACK2; + } + break; + + case AJSPECIAL_KEY_ATTACK3: + + if (player_keys & FLAG_ATTACK3) + { + thekey = FLAG_ATTACK3; + } + break; + + case AJSPECIAL_KEY_ATTACK4: + + if (player_keys & FLAG_ATTACK4) + { + thekey = FLAG_ATTACK4; + } + break; + + default: + thekey = 0; + } + + if (!thekey) + { + return 0; + } + + if(check_special()) + { + self->stalltime = 0; + player[self->playerindex].playkeys &= ~thekey; + return 1; + } + else + { + return 0; + } +} + + +void common_land() +{ + self->velocity.x = self->velocity.z = 0; + if(self->animating) + { + return; + } + + self->takeaction = NULL; + set_idle(self); +} + + +//animal run when you lost it 3 times by tails +void runanimal() +{ + common_walk_anim(self); + //ent_set_anim(self, ANI_WALK, 0); + + if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80)) + { + kill_entity(self, KILL_ENTITY_TRIGGER_ANIMAL_RUN_OUT_OF_BOUNDS); + return; + } + + if(self->direction == DIRECTION_RIGHT) + { + self->position.x += self->modeldata.speed.x; + } + else + { + self->position.x -= self->modeldata.speed.x; + } +} + + +void player_blink() +{ + self->blink = 1; + if(_time >= self->stalltime) + { + player_die(); + } +} + + +void common_grabattack() +{ + if(self->animating) + { + return; + } + + self->attacking = ATTACKING_NONE; + + if(!(self->combostep[0] || self->combostep[1] || + self->combostep[2] || self->combostep[3] || + self->combostep[4])) + { + ent_unlink(self); + } + + if(self->link) + { + self->takeaction = common_grab; + self->link->takeaction = common_grabbed; + self->attacking = ATTACKING_NONE; + ent_set_anim(self, ANI_GRAB, 0); + set_pain(self->link, -1, 0); + update_frame(self, self->animation->numframes - 1); + update_frame(self->link, self->link->animation->numframes - 1); + } + else + { + self->takeaction = NULL; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + set_idle(self); + } +} + +// Function that causes the player to continue to move up or down until the animation has finished playing +void common_dodge() // New function so players can dodge with up up or down down +{ + if(self->animating) // Continues to move as long as the player is animating + { + return; + } + else // Once done animating, returns to thinking + { + self->takeaction = NULL; + self->velocity.x = self->velocity.z = 0; + set_idle(self); + } +} + + +void common_preduck() +{ + if(self->animating) + { + return; + } + doduck(self); +} + + +void common_idle() +{ + if(self->animating) + { + return; + } + self->takeaction = NULL; + self->attacking = ATTACKING_NONE; + self->idling = IDLING_PREPARED; + common_idle_anim(self); +} + + +void tryduck(entity *ent) +{ + ent->running = RUN_STATE_NONE; + if(validanim(ent, ANI_DUCKING)) + { + ent->takeaction = common_preduck; + ent->velocity.x = ent->velocity.z = 0; + ent->ducking = DUCK_PREPARED; + ent->idling = IDLING_NONE; + ent_set_anim(ent, ANI_DUCKING, 0); + } + else + { + doduck(ent); + } +} + + +void tryduckrise(entity *ent) +{ + ent->running = RUN_STATE_NONE; + if(validanim(ent, ANI_DUCKRISE)) + { + ent->takeaction = common_idle; + ent->velocity.x = ent->velocity.z = 0; + ent->ducking = DUCK_RISE; + ent->idling = IDLING_NONE; + ent_set_anim(ent, ANI_DUCKRISE, 0); + } + else + { + ent->takeaction = NULL; + ent->idling = IDLING_PREPARED; + common_idle_anim(self); + } +} + + +void doduck(entity *ent) +{ + ent->takeaction = NULL; + ent->velocity.x = ent->velocity.z = 0; + ent->ducking = DUCK_ACTIVE; + ent->idling |= IDLING_PREPARED; + ent_set_anim(ent, ANI_DUCK, 0); +} + + +void common_prejump() +{ + if(self->animating) + { + return; + } + dojump(self->jump.velocity.y, self->jump.velocity.x, self->jump.velocity.z, self->jump.animation_id); +} + + +void tryjump(float jumpv, float jumpx, float jumpz, animation_id_t animation_id) +{ + self->jump.velocity.y = jumpv; + self->jump.velocity.x = jumpx; + self->jump.velocity.z = jumpz; + self->jump.animation_id = animation_id; + + self->ducking = DUCK_NONE; + if(validanim(self, ANI_JUMPDELAY)) + { + self->takeaction = common_prejump; + self->velocity.x = self->velocity.z = 0; + + self->idling = IDLING_NONE; + ent_set_anim(self, ANI_JUMPDELAY, 0); + } + else + { + dojump(jumpv, jumpx, jumpz, animation_id); + } +} + + +void dojump(float jumpv, float jumpx, float jumpz, animation_id_t animation_id) +{ + entity *dust; + + self->takeaction = common_jump; + + if(global_sample_list.jump >= 0) + { + sound_play_sample(global_sample_list.jump, 0, savedata.effectvol, savedata.effectvol, 100); + } + + //Spawn jumpstart dust. + if(self->modeldata.dust.jump_start >= 0) + { + dust = spawn(self->position.x, self->position.z, self->position.y, self->direction, NULL, self->modeldata.dust.jump_start, NULL); + if(dust) + { + dust->spawntype = SPAWN_TYPE_DUST_JUMP; + dust->base = self->position.y; + dust->autokill |= AUTOKILL_ANIMATION_COMPLETE; + execute_onspawn_script(dust); + } + } + + set_jumping(self); + + toss(self, jumpv); + + if(self->direction == DIRECTION_LEFT) + { + self->velocity.x = -jumpx; + } + else + { + self->velocity.x = jumpx; + } + + self->velocity.z = jumpz; + ent_set_anim(self, animation_id, 0); +} + +// Function created to combine the action taken if either picking up an item, or running into an item that is a +// SUBTYPE_TOUCH, executing the appropriate action based on which type of item is picked up +void didfind_item(entity *other) +{ + // Function that takes care of items when picked up + set_opponent(self, other); + + /* + * Legacy limited use replenish. + * + * If item has a use_add value and collecting + * entity has a limited use weapon, we add + * use_add to uses remaining. + */ + + if(other->modeldata.weapon_properties.use_add) + { + if(self->weapent && self->weapent->modeldata.weapon_properties.weapon_state & WEAPON_STATE_LIMITED_USE) + { + self->weapent->modeldata.weapon_properties.use_count += other->modeldata.weapon_properties.use_add; + + if(global_sample_list.get >= 0) + { + sound_play_sample(global_sample_list.get, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else + { + addscore(self->playerindex, other->modeldata.score); + if(global_sample_list.get_2 >= 0) + { + sound_play_sample(global_sample_list.get_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + } + + //end of weapons items section + else if(other->modeldata.score) + { + addscore(self->playerindex, other->modeldata.score); + if(global_sample_list.get_2 >= 0) + { + sound_play_sample(global_sample_list.get_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->energy_state.health_current) + { + self->energy_state.health_current += other->energy_state.health_current; + + if(self->energy_state.health_current > self->modeldata.health) + { + self->energy_state.health_current = self->modeldata.health; + } + + other->energy_state.health_current = 0; + + if(global_sample_list.get >= 0) + { + sound_play_sample(global_sample_list.get, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.mp) + { + self->energy_state.mp_current += other->modeldata.mp; + + if(self->energy_state.mp_current > self->modeldata.mp) + { + self->energy_state.mp_current = self->modeldata.mp; + } + + other->energy_state.mp_current = 0; + sound_play_sample(global_sample_list.get, 0, savedata.effectvol, savedata.effectvol, 100); + } + else if(stricmp(other->modeldata.name, "Time") == 0) + { + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + + if(global_sample_list.get_2 >= 0) + { + sound_play_sample(global_sample_list.get_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.makeinv) + { + // Mar 2, 2005 - New item makes player invincible + self->invincible |= INVINCIBLE_INTANGIBLE; + self->invinctime = _time + ABS(other->modeldata.makeinv); + self->blink = (other->modeldata.makeinv > 0); + + if(global_sample_list.get_2 >= 0) + { + sound_play_sample(global_sample_list.get_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.smartbomb) + { + // Damages everything on the screen + smart_bomb(self, other->modeldata.smartbomb); + + if(global_sample_list.get_2 >= 0) + { + sound_play_sample(global_sample_list.get_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.subtype == SUBTYPE_WEAPON) + { + dropweapon(self, 0); + self->weapent = other; + set_weapon(self, other->modeldata.weapon_properties.weapon_index, 0); + + /* Move to weapon location if it's an "animal". */ + if(self->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL) + { + self->direction = other->direction; + self->position.x = other->position.x; + self->position.z = other->position.z; + } + + if(!(other->modeldata.weapon_properties.weapon_state & WEAPON_STATE_LIMITED_USE) && self->modeldata.weapon_properties.weapon_state & WEAPON_STATE_LIMITED_USE) + { + other->modeldata.weapon_properties.weapon_state |= WEAPON_STATE_LIMITED_USE; + } + + if(global_sample_list.get >= 0) + { + sound_play_sample(global_sample_list.get, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.subtype == SUBTYPE_PROJECTILE) + { + dropweapon(self, 0); + self->weapent = other; + + if(global_sample_list.get >= 0) + { + sound_play_sample(global_sample_list.get, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if(other->modeldata.credit) + { + if(!noshare) + { + credits++; + } + else + { + player[self->playerindex].credits++; + } + + if(global_sample_list.one_up >= 0) + { + sound_play_sample(global_sample_list.one_up, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else + { + // Must be a 1up then. + player[self->playerindex].lives++; + + if(global_sample_list.one_up >= 0) + { + sound_play_sample(global_sample_list.one_up, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + + if(other->modeldata.subtype != SUBTYPE_WEAPON && other->modeldata.subtype != SUBTYPE_PROJECTILE) + { + other->takeaction = suicide; + if(!other->modeldata.instantitemdeath) + { + other->nextthink = _time + global_config.game_speed * 3; + } + } + else + { + if(!(other->nextanim & DELAY_FLAG_INFINITE)) { + other->nextanim = animation_timestamp_add_bounded( + _time, + delay_multiply_bounded( + global_config.game_speed, + UINT64_C(999999) + ) + ); + } + other->nextthink = _time + global_config.game_speed * 999999; + } + other->position.z = ITEM_HIDE_POSITION_Z; +} + +void player_fall_check() +{ + if(autoland != 2 && (player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) == (FLAG_MOVEUP | FLAG_JUMP)) + { + // mark it, so we will play land animation when hit the ground. + self->damage_on_landing.attack_force = ATTACK_FORCE_LAND_COMMAND; + } +} + +void player_grab_check() +{ + entity *other = self->link; + + if(other == NULL || (self->modeldata.grabfinish && self->animating && !self->grabwalking)) + { + return; + } + + if(self->base != other->base) + { + // Change this from ->position.y to ->base + self->takeaction = NULL; + ent_unlink(self); + set_idle(self); + return; + } + + if(player_check_special()) + { + return; + } + + if(!nolost && self->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_GRABBING) + { + dropweapon(self, 1); + } + + // grabturn code + if(self->animation == self->modeldata.animation[ANI_GRABTURN]) + { + // still turning? don't bother with anything else + if(self->animating) + { + return; + } + + // done turning? switch directions and return to grab animation + else + { + if(self->direction == DIRECTION_RIGHT) + { + self->direction = DIRECTION_LEFT; + other->direction = DIRECTION_RIGHT; + } + else + { + self->direction = DIRECTION_RIGHT; + other->direction = DIRECTION_LEFT; + } + other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance); + ent_set_anim(self, ANI_GRAB, 0); + set_pain(other, -1, 0); + update_frame(self, self->animation->numframes - 1); + update_frame(other, other->animation->numframes - 1); + } + } + + self->attacking = ATTACKING_NONE; //for checking + self->grabwalking = 0; + + // Move key opposite vs. dicretion? + if(self->direction == DIRECTION_RIGHT ? (player[self->playerindex].keys & FLAG_MOVELEFT) : (player[self->playerindex].keys & FLAG_MOVERIGHT)) + { + // initiating grabturn + if(self->modeldata.grabturn) + { + // start animation if it exists... + if(validanim(self, ANI_GRABTURN)) + { + ent_set_anim(self, ANI_GRABTURN, 0); + if(validanim(other, ANI_GRABBEDTURN)) + { + ent_set_anim(other, ANI_GRABBEDTURN, 0); + } + else if(validanim(other, ANI_GRABBED)) + { + ent_set_anim(other, ANI_GRABBED, 0); + } + else + { + if ( ((!other->direction && self->position.x > other->position.x) || (other->direction && self->position.x < other->position.x)) && validanim(other, ANI_BACKPAIN) ) ent_set_anim(other, ANI_BACKPAIN, 0); + else ent_set_anim(other, ANI_PAIN, 0); + } + other->velocity.x = other->velocity.z = self->velocity.x = self->velocity.z = 0; + other->position.x = self->position.x; + return; + } + + // otherwise, just turn around + else + { + if(self->direction == DIRECTION_RIGHT) + { + self->direction = DIRECTION_LEFT; + other->direction = DIRECTION_RIGHT; + } + else + { + self->direction = DIRECTION_RIGHT; + other->direction = DIRECTION_LEFT; + } + ent_set_anim(self, ANI_GRAB, 0); + set_pain(other, -1, 0); + update_frame(self, self->animation->numframes - 1); + update_frame(other, other->animation->numframes - 1); + other->position.x = self->position.x + (((self->direction * 2) - 1) * self->modeldata.grabdistance); + } + } + else if(!validanim(self, ANI_GRABWALK) && _time > self->releasetime) + { + // Release + self->takeaction = NULL; + ent_unlink(self); + set_idle(self); + return; + } + } + else + { + self->releasetime = _time + (global_config.game_speed / 2); + } + + if((player[self->playerindex].playkeys & FLAG_ATTACK) && + (self->direction ? + (player[self->playerindex].keys & FLAG_MOVELEFT) : + (player[self->playerindex].keys & FLAG_MOVERIGHT))) + { + player[self->playerindex].playkeys -= FLAG_ATTACK; + if(validanim(self, ANI_GRABBACKWARD)) + { + dograbattack(GRAB_ACTION_SELECT_BACKWARD); + } + else if(validanim(self, ANI_THROW)) + { + if(self->modeldata.throwframewait >= 0) + { + doprethrow(); + } + else + { + dothrow(); + } + } + else + { + dograbattack(GRAB_ACTION_SELECT_ATTACK); + } + } + // grab forward + else if((player[self->playerindex].playkeys & FLAG_ATTACK) && + validanim(self, ANI_GRABFORWARD) && + (!self->direction ? + (player[self->playerindex].keys & FLAG_MOVELEFT) : + (player[self->playerindex].keys & FLAG_MOVERIGHT))) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + dograbattack(GRAB_ACTION_SELECT_FORWARD); + } + // grab up + else if((player[self->playerindex].playkeys & FLAG_ATTACK) && + validanim(self, ANI_GRABUP) && (player[self->playerindex].keys & FLAG_MOVEUP)) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + dograbattack(GRAB_ACTION_SELECT_UP); + } + // grab down + else if((player[self->playerindex].playkeys & FLAG_ATTACK) && + validanim(self, ANI_GRABDOWN) && (player[self->playerindex].keys & FLAG_MOVEDOWN)) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + dograbattack(GRAB_ACTION_SELECT_DOWN); + } + // normal grab attack + else if((player[self->playerindex].playkeys & FLAG_ATTACK) && validanim(self, ANI_GRABATTACK)) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + dograbattack(GRAB_ACTION_SELECT_ATTACK); + } + // Kratus (10-2021) Added the vault animation + else if(player[self->playerindex].playkeys & FLAG_JUMP && validanim(self, ANI_VAULT)) + { + // Kratus (07-2022) Fixed the "3-vaults" loop bug, now will execute once as intended + // Kratus (01-2023) Fixed an issue where grabattack2 animation nullifies vault if both are declared + // Kratus (01-2023) Added some native behaviours + player[self->playerindex].playkeys &= ~FLAG_JUMP; + self->attacking = ATTACKING_ACTIVE; + self->takeaction = common_grabattack; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + ent_set_anim(self, ANI_VAULT, 0); + } + // grab attack finisher + else if(player[self->playerindex].playkeys & (FLAG_ATTACK | FLAG_JUMP)) + { + player[self->playerindex].playkeys &= ~(FLAG_ATTACK | FLAG_JUMP); + + // Perform final blow + if(validanim(self, ANI_GRABATTACK2) || validanim(self, ANI_ATTACK3)) + { + dograbattack(GRAB_ACTION_SELECT_FINISH); + } + else + { + self->attacking = ATTACKING_ACTIVE; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + self->takeaction = common_grabattack; + tryjump(self->modeldata.jumpheight, self->modeldata.jumpspeed, 0, ANI_JUMP); + } + } + + // grab walk code + else if(validanim(self, ANI_GRABWALK) // check if grabwalk animation exists + + // if entity is still animating anything besides a grabwalk variant, don't let them move + && (!self->animating || self->animation == self->modeldata.animation[ANI_GRABWALK] + || self->animation == self->modeldata.animation[ANI_GRABWALKUP] + || self->animation == self->modeldata.animation[ANI_GRABWALKDOWN] + || self->animation == self->modeldata.animation[ANI_GRABBACKWALK])) + { + + // z axis movement + if(PLAYER_MIN_Z != PLAYER_MAX_Z) + { + if(player[self->playerindex].keys & FLAG_MOVEUP) + { + if(self->modeldata.grabwalkspeed) + { + self->velocity.z = -self->modeldata.grabwalkspeed / 2; + } + else + { + self->velocity.z = -self->modeldata.speed.x / 2; + } + } + else if(player[self->playerindex].keys & FLAG_MOVEDOWN) + { + if(self->modeldata.grabwalkspeed) + { + self->velocity.z = self->modeldata.grabwalkspeed / 2; + } + else + { + self->velocity.z = self->modeldata.speed.x / 2; + } + } + else if(!(player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN))) + { + self->velocity.z = 0; + } + } + + // x axis movement + if(player[self->playerindex].keys & FLAG_MOVELEFT) + { + if(self->modeldata.grabwalkspeed) + { + self->velocity.x = -self->modeldata.grabwalkspeed; + } + else + { + self->velocity.x = -self->modeldata.speed.x; + } + } + + else if(player[self->playerindex].keys & FLAG_MOVERIGHT) + { + if(self->modeldata.grabwalkspeed) + { + self->velocity.x = self->modeldata.grabwalkspeed; + } + else + { + self->velocity.x = self->modeldata.speed.x; + } + } + else if(!((player[self->playerindex].keys & FLAG_MOVELEFT) || (player[self->playerindex].keys & FLAG_MOVERIGHT)) ) + { + self->velocity.x = 0; + } + + // setting the animations based on the velocity set above + if(self->velocity.x || self->velocity.z) + { + if(((self->velocity.x > 0 && self->direction == DIRECTION_LEFT) || (self->velocity.x < 0 && self->direction == DIRECTION_RIGHT)) && validanim(self, ANI_GRABBACKWALK)) + { + ent_set_anim(self, ANI_GRABBACKWALK, 0); + } + else if(self->velocity.z < 0 && validanim(self, ANI_GRABWALKUP)) + { + ent_set_anim(self, ANI_GRABWALKUP, 0); + } + else if(self->velocity.z > 0 && validanim(self, ANI_GRABWALKDOWN)) + { + ent_set_anim(self, ANI_GRABWALKDOWN, 0); + } + else + { + ent_set_anim(self, ANI_GRABWALK, 0); + } + if(self->animation == self->modeldata.animation[ANI_GRABWALKUP] && validanim(other, ANI_GRABBEDWALKUP)) + { + ent_set_anim(other, ANI_GRABBEDWALKUP, 0); + } + else if(self->animation == self->modeldata.animation[ANI_GRABWALKDOWN] && validanim(other, ANI_GRABBEDWALKDOWN)) + { + ent_set_anim(other, ANI_GRABBEDWALKDOWN, 0); + } + else if(self->animation == self->modeldata.animation[ANI_GRABBACKWALK] && validanim(other, ANI_GRABBEDBACKWALK)) + { + ent_set_anim(other, ANI_GRABBEDBACKWALK, 0); + } + else if(validanim(other, ANI_GRABBEDWALK)) + { + ent_set_anim(other, ANI_GRABBEDWALK, 0); + } + else if (validanim(other, ANI_GRABBED)) + { + ent_set_anim(other, ANI_GRABBED, 0); + } + else + { + ent_set_anim(other, ANI_PAIN, 0); + } + } + else + { + ent_set_anim(self, ANI_GRAB, 0); + if (validanim(other, ANI_GRABBED)) + { + ent_set_anim(other, ANI_GRABBED, 0); + } + else + { + ent_set_anim(other, ANI_PAIN, 0); + } + } + // use check_link_move to set velocity, don't change it here + other->velocity.z = other->velocity.x = 0; + self->grabwalking = 1; + } + + if(self->attacking != ATTACKING_NONE) + { + self->releasetime = _time + (global_config.game_speed / 2); // reset releasetime when do collision + } +} + +void player_walkoff_check() +{ + /* + * Turn. + */ + if(self->modeldata.air_control & AIR_CONTROL_WALKOFF_TURN) + { + if((player[self->playerindex].keys & FLAG_MOVELEFT)) + { + self->direction = DIRECTION_LEFT; + } + else if((player[self->playerindex].keys & FLAG_MOVERIGHT)) + { + self->direction = DIRECTION_RIGHT; + } + } + + /* + * X stop. If not holding a Left or + * Right key, kill horizontal momentum. + */ + if (self->modeldata.air_control & AIR_CONTROL_WALKOFF_X_STOP) + { + if (!(player[self->playerindex].keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))) + { + if (self->velocity.x != 0) + { + self->velocity.x *= AIR_CONTROL_STOP_FACTOR; + } + } + } + + /* + * Z stop. If not holding an Up or + * Down key, kill lateral momentum. + */ + if (self->modeldata.air_control & AIR_CONTROL_WALKOFF_Z_STOP) + { + if (!(player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN))) + { + if (self->velocity.z != 0) + { + self->velocity.z *= AIR_CONTROL_STOP_FACTOR; + } + } + } + + /* Horizontal move control (if already moving). */ + if(self->modeldata.air_control & AIR_CONTROL_WALKOFF_X_ADJUST) //move? + { + if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) || + ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0)) + { + self->velocity.x = -self->velocity.x; + } + } + + /* Horizontal move control (any walkoff). */ + if(self->modeldata.air_control & AIR_CONTROL_WALKOFF_X_MOVE) + { + if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) || + ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0)) + { + self->velocity.x = -self->velocity.x; + } + + if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x)) + { + self->velocity.x -= self->modeldata.speed.x; + } + else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x)) + { + self->velocity.x = self->modeldata.speed.x; + } + } + + /* Z move control (if already moving). */ + if(self->modeldata.air_control & AIR_CONTROL_WALKOFF_Z_ADJUST) + { + if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) || + ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0)) + { + self->velocity.z = -self->velocity.z; + } + } + + /* Z move control (any walkoff). */ + if(self->modeldata.air_control & AIR_CONTROL_WALKOFF_Z_MOVE) + { + if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z)) + { + self->velocity.z -= (0.5 * self->modeldata.speed.x); + } + else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z)) + { + self->velocity.z = (0.5 * self->modeldata.speed.x); + } + } + + return; +} + +void player_jump_check() +{ + int candospecial = 0; + + // Kratus (10-2021) Fixed the noaircancel function + // Now the flag "2" works as intended and completely disables the cancelation between all jumping attacks + // In the previous code, both flags 1 and 2 have the same effect and allow the cancelation after last jumpattack is finished + if((!noaircancel) || (noaircancel&1 && !self->animating) || (noaircancel&3 && (self->animnum == self->jump.animation_id))) + { + //air special, copied and changed from Fugue's code + if((!level->nospecial || level->nospecial == 3) && player[self->playerindex].playkeys & FLAG_SPECIAL) + { + if(validanim(self, ANI_JUMPSPECIAL)) + { + // Kratus (10-2021) For safe, added an new step to check if the entity is not in the "jumpspecial" animation + // Fixes the bug that constantly consumes health or mp if the special button is constantly pressed, even if + // the current "jumpspecial" animation cycle is not finished yet + if(self->animnum != ANI_JUMPSPECIAL) + { + if(self->modeldata.animation[ANI_JUMPSPECIAL]->energy_cost.cost && check_energy(ENERGY_TYPE_MP, ANI_JUMPSPECIAL)) + { + // Kratus (10-2021) Now the "infinite health cheat" will only work when the cost is HEALTH, will not work when the cost is MP anymore + self->energy_state.mp_current -= self->modeldata.animation[ANI_JUMPSPECIAL]->energy_cost.cost; + candospecial = 1; + } + else if(self->modeldata.animation[ANI_JUMPSPECIAL]->energy_cost.cost && check_energy(ENERGY_TYPE_HP, ANI_JUMPSPECIAL)) + { + if(!(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE)) + { + self->energy_state.health_current -= self->modeldata.animation[ANI_JUMPSPECIAL]->energy_cost.cost; + } + candospecial = 1; + } + else if(validanim(self, ANI_JUMPCANT)) + { + ent_set_anim(self, ANI_JUMPCANT, 0); + self->velocity.y = 0; + } + } + + if(candospecial) + { + player[self->playerindex].playkeys &= ~FLAG_SPECIAL; + self->attacking = ATTACKING_ACTIVE; + + // Kratus (10-2021) Add a option to kill or not the xyz movement + // Kratus (04-2022) Minor fix on the jumpspecial code + if(!(self->modeldata.jumpspecial & 1)) + { + self->velocity.x = self->velocity.z = 0; // Kill movement when the special starts + self->velocity.y = 0; + } + + ent_set_anim(self, ANI_JUMPSPECIAL, 0); + } + } + }//end of jumpspecial + + //jumpattacks, up down forward normal....we don't check energy cost + else if(player[self->playerindex].playkeys & FLAG_ATTACK) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + self->attacking = ATTACKING_ACTIVE; + + if((player[self->playerindex].keys & FLAG_MOVEDOWN) && validanim(self, ANI_JUMPATTACK2)) + { + ent_set_anim(self, ANI_JUMPATTACK2, 0); + } + else if((player[self->playerindex].keys & FLAG_MOVEUP) && validanim(self, ANI_JUMPATTACK3)) + { + ent_set_anim(self, ANI_JUMPATTACK3, 0); + } + else if(self->running && validanim(self, ANI_RUNJUMPATTACK)) + { + ent_set_anim(self, ANI_RUNJUMPATTACK, 0); // Added so an extra strong jump attack can be executed + } + else if(self->velocity.x != 0 && validanim(self, ANI_JUMPFORWARD)) + { + ent_set_anim(self, ANI_JUMPFORWARD, 0); // If moving and set, do this attack + } + else if(validanim(self, ANI_JUMPATTACK)) + { + ent_set_anim(self, ANI_JUMPATTACK, 0); + } + }//end of jumpattack + } + + /* + * Jump height control. Stop rising if jump + * key is inactive. + */ + if (self->modeldata.air_control & AIR_CONTROL_JUMP_Y_STOP) + { + if (!(player[self->playerindex].keys & FLAG_JUMP)) + { + if (self->velocity.y > 0) + { + self->velocity.y *= AIR_CONTROL_STOP_FACTOR; + } + } + } + + /* + * Jump X stop. If not holding a Left or + * Right key, kill horizontal momentum. + */ + if (self->modeldata.air_control & AIR_CONTROL_JUMP_X_STOP) + { + if (!(player[self->playerindex].keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))) + { + if (self->velocity.x != 0) + { + self->velocity.x *= AIR_CONTROL_STOP_FACTOR; + } + } + } + + /* + * Jump Z stop. If not holding an Up or + * Down key, kill lateral momentum. + */ + if (self->modeldata.air_control & AIR_CONTROL_JUMP_Z_STOP) + { + if (!(player[self->playerindex].keys & (FLAG_MOVEUP | FLAG_MOVEDOWN))) + { + if (self->velocity.z != 0) + { + self->velocity.z *= AIR_CONTROL_STOP_FACTOR; + } + } + } + + /* Jump turn control. */ + if(self->modeldata.air_control & AIR_CONTROL_JUMP_TURN) + { + if((player[self->playerindex].keys & FLAG_MOVELEFT)) + { + self->direction = DIRECTION_LEFT; + } + else if((player[self->playerindex].keys & FLAG_MOVERIGHT)) + { + self->direction = DIRECTION_RIGHT; + } + } + + /* Jump horizontal move control (if already moving). */ + if(self->modeldata.air_control & AIR_CONTROL_JUMP_X_ADJUST) + { + if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0 ) || + ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0)) + { + self->velocity.x = -self->velocity.x; + } + } + + /* Jump horizontal move control (any jump). */ + if(self->modeldata.air_control & AIR_CONTROL_JUMP_X_MOVE) + { + if(((player[self->playerindex].keys & FLAG_MOVELEFT) && self->velocity.x > 0) || + ((player[self->playerindex].keys & FLAG_MOVERIGHT) && self->velocity.x < 0)) + { + self->velocity.x = -self->velocity.x; + } + + if((player[self->playerindex].keys & FLAG_MOVELEFT) && (!self->velocity.x)) + { + self->velocity.x -= self->modeldata.speed.x; + } + else if((player[self->playerindex].keys & FLAG_MOVERIGHT) && (!self->velocity.x)) + { + self->velocity.x = self->modeldata.speed.x; + } + } + + /* Jump Z move control (if already moving). */ + if(self->modeldata.air_control & AIR_CONTROL_JUMP_Z_ADJUST) + { + if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) || + ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0)) + { + self->velocity.z = -self->velocity.z; + } + } + + /* Jump Z move control (any jump). */ + if(self->modeldata.air_control & AIR_CONTROL_JUMP_Z_MOVE) + { + if((player[self->playerindex].keys & FLAG_MOVELEFT)) + { + self->direction = DIRECTION_LEFT; + } + else if((player[self->playerindex].keys & FLAG_MOVERIGHT)) + { + self->direction = DIRECTION_RIGHT; + } + + if(((player[self->playerindex].keys & FLAG_MOVEUP) && self->velocity.z > 0) || + ((player[self->playerindex].keys & FLAG_MOVEDOWN) && self->velocity.z < 0)) + { + self->velocity.z = -self->velocity.z; + } + + if((player[self->playerindex].keys & FLAG_MOVEUP) && (!self->velocity.z)) + { + self->velocity.z -= (0.5 * self->modeldata.speed.x); + } + else if((player[self->playerindex].keys & FLAG_MOVEDOWN) && (!self->velocity.z)) + { + self->velocity.z = (0.5 * self->modeldata.speed.x); + } + } + + return; +} + +void player_pain_check() +{ + if(player_check_special()) + { + self->inpain = IN_PAIN_NONE; + self->rising = RISING_NONE; + self->ducking = DUCK_NONE; + self->inbackpain = 0; + } +} + +// check riseattack input up+attack +void player_lie_check() +{ + if(validanim(self, ANI_RISEATTACK) && + (player[self->playerindex].playkeys & FLAG_ATTACK) && + (player[self->playerindex].keys & FLAG_MOVEUP) && + (self->energy_state.health_current > 0 && _time > self->staydown.riseattack_stall)) + { + player[self->playerindex].playkeys &= ~FLAG_ATTACK; + if((player[self->playerindex].keys & FLAG_MOVELEFT)) + { + self->direction = DIRECTION_LEFT; + } + if((player[self->playerindex].keys & FLAG_MOVERIGHT)) + { + self->direction = DIRECTION_RIGHT; + } + self->stalltime = 0; + set_riseattack(self, self->last_damage_type, 0); + } +} + +void player_charge_check() +{ + if(!((player[self->playerindex].keys & FLAG_JUMP) && + (player[self->playerindex].keys & FLAG_SPECIAL))) + { + self->takeaction = NULL; + self->charging = 0; + set_idle(self); + } +} + +// make a function so enemies can use +// UT: jumphack is a temporary fix for jump cancel +int check_costmove(int s, int fs, int jumphack) +{ + if(((fs == 1 && level->nospecial < 2) || (fs == 0 && level->nospecial == 0) || (fs == 0 && level->nospecial == 3)) && + (check_energy(ENERGY_TYPE_HP, s) || + check_energy(ENERGY_TYPE_MP, s)) ) + { + if(!jumphack) + { + self->takeaction = common_attack_proc; + } + // Kratus (10-2021) Now the "infinite health cheat" affects players only, not enemies or npc + // And now the "infinite health cheat" will only work when the cost is HEALTH, will not work when the cost is MP anymore + if(self->modeldata.type & TYPE_PLAYER) + { + if(!nocost) + { + if(!(global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE)) + { + if(self->modeldata.animation[s]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, s)) + { + self->energy_state.mp_current -= self->modeldata.animation[s]->energy_cost.cost; + } + else + { + self->energy_state.health_current -= self->modeldata.animation[s]->energy_cost.cost; + } + } + } + else + { + if(self->modeldata.animation[s]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, s)) + { + self->energy_state.mp_current -= self->modeldata.animation[s]->energy_cost.cost; + } + } + } + } + } + else + { + if(!nocost) + { + if(self->modeldata.animation[s]->energy_cost.cost) + { + if(check_energy(ENERGY_TYPE_MP, s)) + { + self->energy_state.mp_current -= self->modeldata.animation[s]->energy_cost.cost; + } + else + { + self->energy_state.health_current -= self->modeldata.animation[s]->energy_cost.cost; + } + } + } + } + + self->running = RUN_STATE_NONE; + self->velocity.x = self->velocity.z = 0; + set_attacking(self); + self->inpain = IN_PAIN_NONE; + self->rising = RISING_NONE; + self->inbackpain = 0; + memset(self->combostep, 0, sizeof(*self->combostep) * 5); + ent_unlink(self); + ent_set_anim(self, s, 0); + return 1; + } + return 0; +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Record the starting time for every newly pressed +* command input flag. +* +* Physical and direction-relative flags share the same +* 64-bit namespace. Using the flag's bit index provides +* hold timing without a parallel command-history ring. +*/ +static void command_input_hold_start_update( + s_player* acting_player, + const key_mask_t pressed_flags, + const uint64_t event_time +) { + key_mask_t scan_mask = pressed_flags; + + while(scan_mask) { + const uint64_t input_index = + bitmask64_get_lowest_index(scan_mask); + + const key_mask_t input_flag = + bitmask64_from_index(input_index); + + acting_player->command_input_hold_start_time[ + input_index + ] = event_time; + + acting_player->command_input_hold_start_valid |= + input_flag; + + /* + * A new press begins a new continuous hold. Clear + * the automatic-trigger record so time zero can + * trigger again for the new hold. + */ + acting_player->command_input_hold_trigger_valid &= + ~input_flag; + + scan_mask &= ~input_flag; + } +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Collect automatic held-input edges which reach their +* configured threshold on this logical tick. +* +* Thresholds come from the acting entity's configurable +* special commands. A per-input timestamp prevents a +* second input refresh during the same tick from emitting +* the same edge again. Different thresholds for the same +* input remain independent because each is reached on a +* different logical tick during one continuous hold. +*/ +static key_mask_t command_input_hold_trigger_collect( + s_player* acting_player, + const key_mask_t held_flags, + const uint64_t event_time +) { + const entity* acting_entity; + + key_mask_t collected_flags = 0; + + int special_index; + + if(!acting_player || !acting_player->ent) { + return 0; + } + + acting_entity = acting_player->ent; + + for(special_index = 0; + special_index < acting_entity->modeldata.specials_loaded; + special_index++) { + + const s_com* special_command = + &acting_entity->modeldata.special[special_index]; + + int step_index; + + if(special_command->steps <= 0 + || special_command->steps > MAX_SPECIAL_INPUTS) { + continue; + } + + for(step_index = 0; + step_index < special_command->steps; + step_index++) { + + const s_command_input_step* input_step = + &special_command->input[step_index]; + + key_mask_t scan_mask = + input_step->hold_trigger & held_flags; + + while(scan_mask) { + const uint64_t input_index = + bitmask64_get_lowest_index(scan_mask); + + const key_mask_t input_flag = + bitmask64_from_index(input_index); + + const uint64_t hold_start_time = + acting_player->command_input_hold_start_time[ + input_index + ]; + + const bool already_emitted_this_tick = + (acting_player->command_input_hold_trigger_valid + & input_flag) + && acting_player->command_input_hold_trigger_time[ + input_index + ] == event_time; + + if((acting_player->command_input_hold_start_valid + & input_flag) + && hold_start_time <= event_time + && event_time - hold_start_time + == input_step->hold_time + && !already_emitted_this_tick) { + + collected_flags |= input_flag; + } + + scan_mask &= ~input_flag; + } + } + } + + /* + * Record emitted flags only after scanning every + * command. Commands sharing the same input and + * threshold deliberately share one history edge. + */ + { + key_mask_t scan_mask = collected_flags; + + while(scan_mask) { + const uint64_t input_index = + bitmask64_get_lowest_index(scan_mask); + + const key_mask_t input_flag = + bitmask64_from_index(input_index); + + acting_player->command_input_hold_trigger_time[ + input_index + ] = event_time; + + acting_player->command_input_hold_trigger_valid |= + input_flag; + + scan_mask &= ~input_flag; + } + } + + return collected_flags; +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Append one event to a player's command history. +*/ +static void command_input_history_push(s_player* acting_player, const s_command_input_event* input_event) { + + acting_player->command_input_history[acting_player->command_input_index] = *input_event; + + acting_player->command_input_index = (acting_player->command_input_index + 1) & SPECIAL_INPUT_INDEX_MASK; + + if(acting_player->command_input_count < MAX_SPECIAL_INPUTS) { + acting_player->command_input_count++; + } +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Consume the newest press edge from a player's command +* history. +* +* Several hard-coded movement commands consume their +* triggering press. A combined event may also contain a +* release or automatic hold edge, so only remove the +* complete entry when no other edge remains. +*/ +static void command_input_history_consume_latest_press( + s_player* acting_player +) { + s_command_input_event* input_event; + + uint64_t input_index; + + if(!acting_player || !acting_player->command_input_count) { + return; + } + + input_index = + (acting_player->command_input_index - 1) + & SPECIAL_INPUT_INDEX_MASK; + + input_event = + &acting_player->command_input_history[ + input_index + ]; + + input_event->press = 0; + input_event->press_chord = 0; + + if(input_event->hold || input_event->release) { + return; + } + + acting_player->command_input_index = input_index; + + memset(input_event, 0, sizeof(*input_event)); + + acting_player->command_input_count--; +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Consume selected automatic held-input flags from the +* event which triggered a configurable command. +* +* Other flags remain available when the same history +* entry also contains a press, release, or another held +* edge. Empty non-latest entries stay in place so ring +* order and stored event ages do not need compaction. +*/ +static void command_input_history_consume_hold_trigger( + s_player* acting_player, + const uint64_t event_age, + const key_mask_t trigger_flags +) { + s_command_input_event* input_event; + + uint64_t input_index; + + if(!acting_player + || !trigger_flags + || event_age >= acting_player->command_input_count) { + return; + } + + input_index = + (acting_player->command_input_index - event_age - 1) + & SPECIAL_INPUT_INDEX_MASK; + + input_event = + &acting_player->command_input_history[input_index]; + + input_event->hold &= ~trigger_flags; + + if(input_event->press + || input_event->hold + || input_event->release + || event_age) { + return; + } + + acting_player->command_input_index = input_index; + + memset(input_event, 0, sizeof(*input_event)); + + acting_player->command_input_count--; +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Read the preceding valid event from a player's +* command history. +*/ +static const s_command_input_event* command_input_history_previous( + const s_player* acting_player, + uint64_t* history_index, + uint64_t* remaining_events +) { + if(!*remaining_events) { + return NULL; + } + + *history_index = + (*history_index - 1) & SPECIAL_INPUT_INDEX_MASK; + + (*remaining_events)--; + + return &acting_player->command_input_history[ + *history_index + ]; +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Read the preceding press event from a player's +* command history. +* +* Non-press entries are skipped so release and timed +* hold events can be added without changing legacy +* hard-coded command behavior. +*/ +static const s_command_input_event* command_input_history_previous_press( + const s_player* acting_player, + uint64_t* history_index, + uint64_t* remaining_events +) { + const s_command_input_event* input_event; + + while((input_event = command_input_history_previous( + acting_player, + history_index, + remaining_events + ))) { + + if(input_event->press) { + return input_event; + } + } + + return NULL; +} + +/* +* Check whether the player's most recent press +* events satisfy a hard-coded command sequence. +* +* Each sequence entry is a bit mask of required +* inputs. A recorded press may contain additional +* flags and still match. +* +* Release and timed hold events are deliberately +* ignored so existing hard-coded commands retain +* their positive-edge behavior. +*/ +static bool match_combo(const key_mask_t sequence[], const s_player* acting_player, const uint64_t length) { + const s_command_input_event* input_event; + + uint64_t history_index; + uint64_t newer_event_time; + uint64_t remaining_events; + uint64_t sequence_index; + + if(!sequence + || !acting_player + || !length + || length > MAX_SPECIAL_INPUTS) { + return false; + } + + history_index = acting_player->command_input_index; + newer_event_time = _time; + remaining_events = acting_player->command_input_count; + + for(sequence_index = length; + sequence_index > 0; + sequence_index--) { + + const key_mask_t required_press = + sequence[sequence_index - 1]; + + input_event = command_input_history_previous_press( + acting_player, + &history_index, + &remaining_events + ); + + if(!input_event + || (input_event->press & required_press) + != required_press + || newer_event_time - input_event->time + > global_config.command_time) { + return false; + } + + newer_event_time = input_event->time; + } + + return true; +} + +/* +* Return the grace time used by one configurable command +* sequence. Commands without an explicit override inherit +* the engine-wide setting at match time. +*/ +static uint64_t command_sequence_grace_time_get( + const s_com* special_command +) { + assert(special_command); + + return special_command->sequence_grace_time_override + ? special_command->sequence_grace_time + : global_config.command_time; +} + +/* +* Return the longest grace time needed by the acting +* entity's configurable and hard-coded commands. +* +* Input history is shared by every command. Retaining it +* for the longest configured grace prevents a command +* with a longer local override from losing its earlier +* steps to the global history timeout. +*/ +static uint64_t command_input_history_grace_time_get( + const entity* acting_entity +) { + uint64_t history_grace_time = global_config.command_time; + + int special_index; + + if(!acting_entity) { + return history_grace_time; + } + + for(special_index = 0; + special_index < acting_entity->modeldata.specials_loaded; + special_index++) { + + const s_com* special_command = + &acting_entity->modeldata.special[special_index]; + + const uint64_t sequence_grace_time = + command_sequence_grace_time_get(special_command); + + if(sequence_grace_time > history_grace_time) { + history_grace_time = sequence_grace_time; + } + } + + return history_grace_time; +} + +/* +* Return the absolute logical tick when shared command +* history expires. Saturation preserves extremely large +* creator-defined grace values without wrapping the +* expiration time around to the beginning of the clock. +*/ +static uint64_t command_input_history_expiration_time_get( + const entity* acting_entity, + const uint64_t event_time +) { + const uint64_t history_grace_time = + command_input_history_grace_time_get(acting_entity); + + if(history_grace_time > UINT64_MAX - event_time) { + return UINT64_MAX; + } + + return event_time + history_grace_time; +} + +/* +* Return whether an input event satisfies the positive +* edge requirement for one configurable command step. +* +* Single-key steps retain exact legacy behavior and use +* only the physical press edge. Multi-key steps use the +* chord snapshot and their configured chord_time. Zero +* requires every member to begin on the same logical tick. +* At least one required key must be physically pressed in +* the triggering event, preventing an unrelated third key +* from retriggering a chord which merely remains held. +*/ +static bool command_input_event_matches_press( + const s_command_input_step* input_step, + const s_command_input_event* input_event, + const s_player* acting_player +) { + const key_mask_t required_press = input_step->press; + + key_mask_t scan_mask; + + if(!required_press) { + return true; + } + + /* + * A mask with one active bit has no second bit after + * removing its lowest bit. + */ + if(!(required_press & (required_press - 1))) { + return (input_event->press & required_press) + == required_press; + } + + if(!(input_event->press & required_press) + || (input_event->press_chord & required_press) + != required_press) { + return false; + } + + scan_mask = required_press; + + while(scan_mask) { + const uint64_t input_index = + bitmask64_get_lowest_index(scan_mask); + + const key_mask_t input_flag = + bitmask64_from_index(input_index); + + const uint64_t press_time = + acting_player->command_input_hold_start_time[ + input_index + ]; + + if(!(acting_player->command_input_hold_start_valid + & input_flag) + || press_time > input_event->time + || input_event->time - press_time + > input_step->chord_time) { + return false; + } + + scan_mask &= ~input_flag; + } + + return true; +} + +/* +* Return whether an input event satisfies every passive +* and automatic held requirement for one command step. +*/ +static bool command_input_event_matches_hold( + const s_command_input_step* input_step, + const s_command_input_event* input_event, + const s_player* acting_player +) { + const key_mask_t required_hold_flags = + input_step->hold | input_step->hold_trigger; + + key_mask_t scan_mask; + + if(!required_hold_flags) { + return true; + } + + if((input_event->held & required_hold_flags) + != required_hold_flags) { + return false; + } + + scan_mask = required_hold_flags; + + while(scan_mask) { + const uint64_t input_index = + bitmask64_get_lowest_index(scan_mask); + + const key_mask_t input_flag = + bitmask64_from_index(input_index); + + const uint64_t hold_start_time = + acting_player->command_input_hold_start_time[ + input_index + ]; + + if(!(acting_player->command_input_hold_start_valid + & input_flag) + || hold_start_time > input_event->time) { + return false; + } + + { + const uint64_t held_time = + input_event->time - hold_start_time; + + /* + * Passive holds accept the inclusive minimum + * through the optional inclusive maximum. A + * zero maximum leaves the upper bound open. + * Automatic holds match only their exact + * minimum threshold edge, preventing a later + * threshold for the same key from retriggering + * an earlier automatic command. + */ + if(((input_step->hold & input_flag) + && (held_time < input_step->hold_time + || (input_step->hold_time_maximum + && held_time + > input_step->hold_time_maximum))) + || ((input_step->hold_trigger & input_flag) + && held_time != input_step->hold_time)) { + return false; + } + } + + scan_mask &= ~input_flag; + } + + return true; +} + +/* +* Read the preceding event whose edge type is relevant +* to a configurable command step. +* +* Release-only events do not disturb press-only steps, +* and press-only events do not disturb release-only +* steps. An event of a required type with the wrong key +* is returned so it correctly interrupts the sequence. +*/ +static const s_command_input_event* command_input_history_previous_for_step( + const s_player* acting_player, + const s_command_input_step* input_step, + uint64_t* history_index, + uint64_t* remaining_events +) { + const s_command_input_event* input_event; + + while((input_event = command_input_history_previous( + acting_player, + history_index, + remaining_events + ))) { + + if((input_step->press && input_event->press) + || (input_step->release && input_event->release) + || (input_step->hold_trigger && input_event->hold)) { + return input_event; + } + } + + return NULL; +} + +/* +* Check whether the player's most recent input events +* satisfy a configurable special command. +* +* Press, release, and automatic hold masks are edge +* requirements. Passive hold is a state and minimum +* duration requirement evaluated at the triggering event. +*/ +static bool match_special_command( + const s_command_input_step sequence[], + const s_player* acting_player, + const uint64_t length, + const uint64_t sequence_grace_time, + uint64_t* match_time, + uint64_t* match_age +) { + const s_command_input_event* input_event; + + uint64_t history_index; + uint64_t newer_event_time; + uint64_t remaining_events; + uint64_t sequence_index; + + if(!sequence + || !acting_player + || !match_time + || !match_age + || !length + || length > MAX_SPECIAL_INPUTS) { + return false; + } + + *match_time = 0; + *match_age = UINT64_MAX; + + history_index = acting_player->command_input_index; + newer_event_time = _time; + remaining_events = acting_player->command_input_count; + + for(sequence_index = length; + sequence_index > 0; + sequence_index--) { + + const s_command_input_step* input_step = + &sequence[sequence_index - 1]; + + input_event = command_input_history_previous_for_step( + acting_player, + input_step, + &history_index, + &remaining_events + ); + + if(!input_event + || !command_input_event_matches_press( + input_step, + input_event, + acting_player + ) + || (input_event->release & input_step->release) + != input_step->release + || (input_event->hold & input_step->hold_trigger) + != input_step->hold_trigger + || newer_event_time - input_event->time + > sequence_grace_time + || !command_input_event_matches_hold( + input_step, + input_event, + acting_player + )) { + return false; + } + + if(sequence_index == length) { + *match_time = input_event->time; + *match_age = + acting_player->command_input_count + - remaining_events - 1; + } + + newer_event_time = input_event->time; + } + + return true; +} + +bool check_combo(s_player* acting_player) { + + unsigned int i; + int maxstep = -1; + int maxkeys = -1; + int valid = -1; + + uint64_t min_match_age = UINT64_MAX; + uint64_t max_match_time = 0; + key_mask_t selected_hold_trigger_flags = 0; + + bool match_found = false; + + s_com *com; + + for(i = 0; i < self->modeldata.specials_loaded; i++) { + + com = self->modeldata.special + i; + + if(self->animation->cancel != ANIMATION_CANCEL_DISABLED && + (self->animnum != com->cancel || + com->frame.min > self->animpos || + com->frame.max < self->animpos || + self->animation->hit_count < com->hits)) { + + continue; + + } else if(self->animation->cancel == ANIMATION_CANCEL_DISABLED + && (com->cancel || !player_accepts_idle_input(self) || diff(self->position.y, self->base) > 1)) { + + continue; + } + + { + uint64_t command_match_age; + uint64_t command_match_time; + + const uint64_t sequence_grace_time = + command_sequence_grace_time_get(com); + + if(validanim(self, com->anim) + && (check_energy(ENERGY_TYPE_MP, com->anim) || check_energy(ENERGY_TYPE_HP, com->anim)) + && match_special_command( + com->input, + acting_player, + com->steps, + sequence_grace_time, + &command_match_time, + &command_match_age + )) { + + /* + * Prefer the command triggered by the newest + * input event. Ring age resolves events recorded + * during the same logical tick. Commands sharing + * that event retain the existing longest-sequence, + * then greatest-chord-size ranking. + */ + const bool better_match = + !match_found + || command_match_age < min_match_age + || (command_match_age == min_match_age + && (command_match_time > max_match_time + || (command_match_time == max_match_time + && (com->steps > maxstep + || (com->steps == maxstep + && com->numkeys > maxkeys))))); + + if(!better_match) { + continue; + } + + valid = com->anim; + min_match_age = command_match_age; + max_match_time = command_match_time; + maxstep = com->steps; + maxkeys = com->numkeys; + selected_hold_trigger_flags = + com->input[com->steps - 1].hold_trigger; + match_found = true; + } + } + + } + + if(valid >= 0 && check_costmove(valid, 1, self->jumping)) { + /* + * Automatic held edges are one-shot input events. + * Consume the selected final-step flags only after + * the move starts successfully. Press, release, + * and unrelated automatic flags remain available. + */ + command_input_history_consume_hold_trigger( + acting_player, + min_match_age, + selected_hold_trigger_flags + ); + + return true; + } + + return false; +} + +/* +* Caskey, Damon V. +* 2026-07-26 - Originally written by Fugue (unknown date). +* +* Rework evaluates whether a player +* has a pending command sequence to execute. +*/ +bool player_preinput() { + + s_player *acting_player = player + self->playerindex; + + if(check_combo(acting_player)) { + acting_player->playkeys &= ~FLAG_CONTROLKEYS; + + return true; + } + + return false; +} + + +/* +* Caskey, Damon V. +* 2023-12-29 +* +* Stop running if player +* no longer inputs direction +* that started the run. +*/ +void run_try_runstop_check(entity* acting_entity, const e_RunXDirection movex, const e_RunZDirection movez, const e_RunXDirection running_x, const e_RunZDirection running_z, const int runConfigFlags, const int dashCommandFlag, const int dashFixedFlag, const int enabledFlag, const int stopStateFlag) { + if (!(runConfigFlags & enabledFlag) + || (dashFixedFlag && !acting_entity->animating) + || ((dashCommandFlag && !acting_entity->animating) + || (diff(movex, running_x) && (movez == RUN_DIR_Z_NONE || acting_entity->running & RUN_STATE_START_X)) + || (diff(movez, running_z) && (movex == RUN_DIR_X_NONE || acting_entity->running & RUN_STATE_START_Z)))) { + acting_entity->running = stopStateFlag; + } +} + +/* +* Caskey, Damon V. +* 2023-12-29 +* +* Check player input and run stop +* function if needed. +*/ +void run_try_runstop_player(entity* acting_entity, const s_player* acting_player) +{ + const e_run_state run_state = acting_entity->running; + + if (run_state == RUN_STATE_NONE) + { + return; + } + + /* + * Get player directional input + * from key status. + */ + + const e_RunZDirection movez = (acting_player->keys & FLAG_MOVEUP) ? RUN_DIR_Z_UP : (acting_player->keys & FLAG_MOVEDOWN) ? RUN_DIR_Z_DOWN : RUN_DIR_Z_NONE; + const e_RunXDirection movex = (acting_player->keys & FLAG_MOVELEFT) ? RUN_DIR_X_LEFT : (acting_player->keys & FLAG_MOVERIGHT) ? RUN_DIR_X_RIGHT : RUN_DIR_X_NONE; + + /* + * Get a running direction from + * velocity. + */ + + const e_RunZDirection running_z = run_state ? ((acting_entity->velocity.z < 0) ? RUN_DIR_Z_UP : (acting_entity->velocity.z > 0) ? RUN_DIR_Z_DOWN : RUN_DIR_Z_NONE) : RUN_DIR_Z_NONE; + const e_RunXDirection running_x = run_state ? ((acting_entity->velocity.x < 0) ? RUN_DIR_X_LEFT : (acting_entity->velocity.x > 0) ? RUN_DIR_X_RIGHT : RUN_DIR_X_NONE) : RUN_DIR_X_NONE; + + /* Just for readability below. */ + const e_run_config_flags acting_run_config_flags = acting_entity->modeldata.run_config_flags; + + /* + * Stop running? + * + * 1. X axis direction. + * 2. Able to run in X direction? + * 3. Fixed dash? Fixed dashes stop + * when the animation completes. They + * ignore releasing the key and do not + * stop on a perpendicular axis command + * even if we can't move on the other + * axis. Note the other axis is ignored + * by logic downstream. + * + * 4. Stop on following: + * - No direction command. + * - Direction command opposes current direction. + * - Perpendicular axis command when we don't have the flag enabled. + * + * Repeat check for opposite direction + * and for Z axis. + */ + + switch (running_x) { + case RUN_DIR_X_LEFT: + run_try_runstop_check(acting_entity, movex, movez, running_x, running_z, acting_run_config_flags, RUN_CONFIG_X_LEFT_DASH_COMMAND, RUN_CONFIG_X_LEFT_DASH_FIXED, RUN_CONFIG_X_LEFT_ENABLED, RUN_STATE_NONE); + break; + + case RUN_DIR_X_RIGHT: + run_try_runstop_check(acting_entity, movex, movez, running_x, running_z, acting_run_config_flags, RUN_CONFIG_X_RIGHT_DASH_COMMAND, RUN_CONFIG_X_RIGHT_DASH_FIXED, RUN_CONFIG_X_RIGHT_ENABLED, RUN_STATE_NONE); + break; + + case RUN_DIR_X_NONE: + break; + } + + switch (running_z) { + case RUN_DIR_Z_UP: + run_try_runstop_check(acting_entity, movex, movez, running_x, running_z, acting_run_config_flags, RUN_CONFIG_Z_UP_DASH_COMMAND, RUN_CONFIG_Z_UP_DASH_FIXED, RUN_CONFIG_Z_UP_ENABLED, RUN_STATE_NONE); + break; + + case RUN_DIR_Z_DOWN: + run_try_runstop_check(acting_entity, movex, movez, running_x, running_z, acting_run_config_flags, RUN_CONFIG_Z_DOWN_DASH_COMMAND, RUN_CONFIG_Z_DOWN_DASH_FIXED, RUN_CONFIG_Z_DOWN_ENABLED, RUN_STATE_NONE); + break; + + case RUN_DIR_Z_NONE: + break; + } +} + +/* +* Caskey, Damon V. +* 2026-07-16 +* +* Return true when the entity may +* process ordinary idle-state player +* input. +*/ +bool player_accepts_idle_input(const entity *acting_entity) { + + const s_anim *animation = acting_entity->animation; + + if(acting_entity->idling) { + return true; + } + + if(!animation + || !animation->idle + || animation->numframes <= 0 + || acting_entity->animpos + >= (uint64_t)animation->numframes) { + return false; + } + + return animation->idle[acting_entity->animpos] ? true : false; +} + +void player_think() +{ + typedef enum e_local_action_flags + { + ACTION_NONE, + ACTION_WALK, + ACTION_UP, + ACTION_DOWN, + ACTION_RUN + } e_local_action_flags; + + e_local_action_flags action = 0; + bool back_walk = false; //backwalk + + entity *other = NULL; + float initial_jump_velocity_z = 0.0; + + entity* acting_entity = self; + + const key_mask_t sequence_left_left[] = {FLAG_MOVELEFT, FLAG_MOVELEFT}; + const key_mask_t sequence_right_right[] = {FLAG_MOVERIGHT, FLAG_MOVERIGHT}; + const key_mask_t sequence_up_up[] = {FLAG_MOVEUP, FLAG_MOVEUP}; + const key_mask_t sequence_down_down[] = {FLAG_MOVEDOWN, FLAG_MOVEDOWN}; + + int pli = acting_entity->playerindex; + s_player *acting_player = player + pli; + + if(acting_player->ent != acting_entity || acting_entity->death_state & DEATH_STATE_DEAD) + { + return; + } + + /* + * Are we touching the end level entity? + */ + if((other = find_ent_here(acting_entity, acting_entity->position.x, acting_entity->position.z, TYPE_ENDLEVEL, NULL)) + && diff(acting_entity->position.y, other->position.y) <= 0.1) { + + bool no_player_reached = false; + uint64_t sum_not_reached = 0; + uint64_t sum_reached = 0; + uint64_t i; + + for (i = 0; i < MAX_PLAYERS; i++) { + if (!reached[i]){ + sum_not_reached++; + } + } + no_player_reached = (sum_not_reached >= MAX_PLAYERS) ? true : false; + + if(no_player_reached) { + addscore(pli, other->modeldata.score); + } + reached[pli] = true; + + for (i = 0; i < MAX_PLAYERS; i++) { + sum_reached += reached[i]; + } + + if (!other->modeldata.subtype || (other->modeldata.subtype == SUBTYPE_BOTH && sum_reached >= (count_ents(TYPE_PLAYER)))) { + level_completed = 1; + + if(other->modeldata.branch) { + strncpy( branch_name, other->modeldata.branch, MAX_NAME_LEN); //now, you can branch to another level + } + return; + } + } + + /* + * Reset combo count if time has expired. + */ + if(_time > acting_entity->rush.time) { + acting_entity->rush.count = 0; + acting_entity->rush.time = 0; + } + + if(player_preinput()) { + goto endthinkcheck; + } + + if(acting_entity->charging) { + player_charge_check(); + goto endthinkcheck; + } + + if(acting_entity->inpain & ~IN_PAIN_NONE || (acting_entity->link && !acting_entity->grabbing)) { + player_pain_check(); + goto endthinkcheck; + } + + // falling? check for landing + if(acting_entity->projectile & BLAST_TOSS) { + player_fall_check(); + goto endthinkcheck; + } + + // grab section, dont move if still animating + if(acting_entity->grabbing && acting_entity->attacking == ATTACKING_NONE && acting_entity->takeaction != common_throw_wait) { + player_grab_check(); + goto endthinkcheck; + } + + // jump section + if(acting_entity->jumping) { + player_jump_check(); + goto endthinkcheck; + } + + if(acting_entity->animnum == ANI_WALKOFF) { + player_walkoff_check(); + goto endthinkcheck; + } + + if(acting_entity->drop && acting_entity->position.y == acting_entity->base && !acting_entity->velocity.y) { + player_lie_check(); + goto endthinkcheck; + } + + /* + * Check if player is in a state that allows + * ordinary idle-state input processing. If not, + * skip the rest of the input checks. + */ + if(!player_accepts_idle_input(acting_entity)) { + goto endthinkcheck; + } + + // Check if entity is under a platform + /*if(acting_entity->modeldata.move_config_flags & MOVE_CONFIG_SUBJECT_TO_PLATFORM && (heightvar = acting_entity->animation->size.y ? acting_entity->animation->size.y : acting_entity->modeldata.size.y) && + validanim(acting_entity, ANI_DUCK) && check_platform_between(acting_entity->position.x, acting_entity->position.z, acting_entity->position.y, acting_entity->position.y + heightvar, acting_entity)) + { + acting_entity->idling = IDLING_NONE; + acting_entity->ducking = DUCK_ACTIVE; + acting_entity->takeaction = common_stuck_underneath; + ent_set_anim(acting_entity, ANI_DUCK, 0); + goto endthinkcheck; + }*/ + + const float altdiff = diff(acting_entity->position.y, acting_entity->base); + const bool notinair = (acting_entity->landed_on_platform ? altdiff < 5 : altdiff < 2); + + if(acting_player->playkeys & FLAG_MOVEUP) { + + const bool command_match = (notinair && match_combo(sequence_up_up, acting_player, 2)); + + if(command_match && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_Z_UP_ENABLED | RUN_CONFIG_Z_UP_INITIAL)) == (RUN_CONFIG_Z_UP_ENABLED | RUN_CONFIG_Z_UP_INITIAL) && validanim(acting_entity, ANI_RUN)) { + acting_player->playkeys &= ~FLAG_MOVEUP; + command_input_history_consume_latest_press(acting_player); + acting_entity->running |= RUN_STATE_START_X; // Player begins to run + + } else if(command_match && validanim(acting_entity, ANI_ATTACKUP)) { + + // New u u combo attack + acting_player->playkeys &= ~FLAG_MOVEUP; + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->combostep[0] = 0; + acting_entity->velocity.x = acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_ATTACKUP, 0); + command_input_history_consume_latest_press(acting_player); // this workaround deals default freespecial2 + goto endthinkcheck; + + } else if(command_match && validanim(acting_entity, ANI_DODGE)) { + // New dodge move like on SOR3 + acting_player->playkeys &= ~FLAG_MOVEUP; + acting_entity->takeaction = common_dodge; + acting_entity->combostep[0] = 0; + acting_entity->idling = IDLING_NONE; + acting_entity->velocity.z = -acting_entity->modeldata.speed.x * 1.75; + acting_entity->velocity.x = 0;// OK you can use jumpframe to modify this anyway + ent_set_anim(acting_entity, ANI_DODGE, 0); + command_input_history_consume_latest_press(acting_player); + goto endthinkcheck; + } + } + + if(acting_player->playkeys & FLAG_MOVEDOWN) { + const bool command_match = (notinair && match_combo(sequence_down_down, acting_player, 2)); + + if(command_match && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_DOWN_INITIAL)) == (RUN_CONFIG_Z_DOWN_ENABLED | RUN_CONFIG_Z_DOWN_INITIAL) && validanim(acting_entity, ANI_RUN)) { + acting_player->playkeys &= ~FLAG_MOVEDOWN; + command_input_history_consume_latest_press(acting_player); + acting_entity->running |= RUN_STATE_START_Z; // Player begins to run + + } else if(command_match && validanim(acting_entity, ANI_ATTACKDOWN)) { + // New d d combo attack + acting_player->playkeys &= ~FLAG_MOVEDOWN; + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + ent_set_anim(acting_entity, ANI_ATTACKDOWN, 0); + command_input_history_consume_latest_press(acting_player); + goto endthinkcheck; + + } else if(command_match && validanim(acting_entity, ANI_DODGE)) { + // New dodge move like on SOR3 + acting_player->playkeys &= ~FLAG_MOVEDOWN; + acting_entity->takeaction = common_dodge; + acting_entity->combostep[0] = 0; + acting_entity->idling = IDLING_NONE; + acting_entity->velocity.z = acting_entity->modeldata.speed.x * 1.75; + acting_entity->velocity.x = 0; + ent_set_anim(acting_entity, ANI_DODGE, 0); + command_input_history_consume_latest_press(acting_player); + goto endthinkcheck; + } + } + + if((acting_player->playkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT))) { + + const bool command_match_left = (notinair && (acting_entity->direction == DIRECTION_LEFT && match_combo(sequence_left_left, acting_player, 2))); + const bool command_match_right = (notinair && (acting_entity->direction == DIRECTION_RIGHT && match_combo(sequence_right_right, acting_player, 2))); + const bool command_match_forward = command_match_left || command_match_right; + const bool command_match_back = notinair + && acting_entity->modeldata.facing + && ((acting_entity->direction == DIRECTION_RIGHT && match_combo(sequence_left_left, acting_player, 2)) + || (acting_entity->direction == DIRECTION_LEFT && match_combo(sequence_right_right, acting_player, 2))); + + if (command_match_left && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_X_LEFT_ENABLED | RUN_CONFIG_X_LEFT_INITIAL)) == (RUN_CONFIG_X_LEFT_ENABLED | RUN_CONFIG_X_LEFT_INITIAL) && validanim(acting_entity, ANI_RUN)) { + + acting_player->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both + command_input_history_consume_latest_press(acting_player); + acting_entity->running |= RUN_STATE_START_X; // Player begins to run + + } else if(command_match_right && (acting_entity->modeldata.run_config_flags & (RUN_CONFIG_X_RIGHT_ENABLED | RUN_CONFIG_X_RIGHT_INITIAL)) == (RUN_CONFIG_X_RIGHT_ENABLED | RUN_CONFIG_X_RIGHT_INITIAL) && validanim(acting_entity, ANI_RUN)) { + + acting_player->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both + command_input_history_consume_latest_press(acting_player); + acting_entity->running |= RUN_STATE_START_X; // Player begins to run + + } else if(command_match_back && validanim(acting_entity, ANI_BACKRUN)) { + acting_player->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); // usually left + right is not acceptable, so it is OK to null both + command_input_history_consume_latest_press(acting_player); + acting_entity->running |= RUN_STATE_START_X; // Player begins to run + + } else if(command_match_forward && validanim(acting_entity, ANI_ATTACKFORWARD)) { + acting_player->playkeys &= ~(FLAG_MOVELEFT | FLAG_MOVERIGHT); + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + ent_set_anim(acting_entity, ANI_ATTACKFORWARD, 0); + command_input_history_consume_latest_press(acting_player); + goto endthinkcheck; + } + } + + /* + * Attack both. Attack + Key mapped + * by ajspecial. + */ + + if( (global_config.ajspecial == AJSPECIAL_KEY_SPECIAL && (acting_player->playkeys & FLAG_JUMP) && validanim(acting_entity, ANI_ATTACKBOTH))|| + (global_config.ajspecial == AJSPECIAL_KEY_ATTACK2 && (acting_player->playkeys & FLAG_JUMP) && validanim(acting_entity, ANI_ATTACKBOTH))|| + (global_config.ajspecial == AJSPECIAL_KEY_ATTACK3 && (acting_player->playkeys & FLAG_JUMP) && validanim(acting_entity, ANI_ATTACKBOTH))|| + (global_config.ajspecial == AJSPECIAL_KEY_ATTACK4 && (acting_player->playkeys & FLAG_JUMP) && validanim(acting_entity, ANI_ATTACKBOTH))) + { + if((acting_player->keys & FLAG_ATTACK) && notinair) + { + acting_player->playkeys &= ~FLAG_JUMP; + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + acting_entity->stalltime = 0; // If attack is pressed, holding down attack to execute attack3 is no longer valid + ent_set_anim(acting_entity, ANI_ATTACKBOTH, 0); + goto endthinkcheck; + } + } + + /* + * Command MP Charge. + */ + + if((acting_player->playkeys & FLAG_JUMP) && validanim(acting_entity, ANI_CHARGE)) + { + if((acting_player->keys & FLAG_SPECIAL) && notinair) + { + acting_player->playkeys &= ~FLAG_JUMP; + acting_entity->takeaction = common_charge; + acting_entity->combostep[0] = 0; + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->stalltime = 0; + set_charging(acting_entity); + ent_set_anim(acting_entity, ANI_CHARGE, 0); + goto endthinkcheck; + } + } + + /* + * Handle special key. Check for part of a + * freespecial command, and then blocking. + */ + + if(acting_player->playkeys & FLAG_SPECIAL) // The special button can now be used for freespecials + { + if( validanim(acting_entity, ANI_SPECIAL2) && notinair && + (acting_entity->direction == DIRECTION_LEFT ? + (acting_player->keys & FLAG_MOVELEFT) : + (acting_player->keys & FLAG_MOVERIGHT)) ) + { + if(check_costmove(ANI_SPECIAL2, 0, 0)) + { + acting_player->playkeys &= ~FLAG_SPECIAL; + goto endthinkcheck; + } + } + + // Blocking. + if(validanim(acting_entity, ANI_BLOCK) && notinair && !(acting_entity->modeldata.block_config_flags & BLOCK_CONFIG_DISABLED)) + { + acting_player->playkeys &= ~FLAG_SPECIAL; + + // Set up flags, action, and block animations. + do_active_block(acting_entity); + + goto endthinkcheck; + } + } + + /* + * Breakout special. + */ + + if(notinair && player_check_special()) + { + goto endthinkcheck; // So you don't perform specials falling off the edge + } + + /* + * Charge attack (hold Attack & release). + */ + + if((acting_player->releasekeys & FLAG_ATTACK)) + { + if(acting_entity->stalltime && notinair && + ((validanim(acting_entity, ANI_CHARGEATTACK) && acting_entity->stalltime + (global_config.game_speed * acting_entity->modeldata.animation[ANI_CHARGEATTACK]->charge_time) < _time) || + (!validanim(acting_entity, ANI_CHARGEATTACK) && validanim(acting_entity, animattacks[acting_entity->modeldata.atchain[acting_entity->modeldata.chainlength - 1] - 1]) + && acting_entity->modeldata.chainlength > 0 && acting_entity->stalltime + (global_config.game_speed * acting_entity->modeldata.animation[animattacks[acting_entity->modeldata.atchain[acting_entity->modeldata.chainlength - 1] - 1]]->charge_time) < _time))) + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + + acting_entity->stalltime = 0; + acting_entity->combostep[0] = 0; + + if(global_sample_list.punch >= 0) + { + sound_play_sample(global_sample_list.punch, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(validanim(acting_entity, ANI_CHARGEATTACK)) + { + ent_set_anim(acting_entity, ANI_CHARGEATTACK, 0); + } + else if(validanim(acting_entity, animattacks[acting_entity->modeldata.atchain[acting_entity->modeldata.chainlength - 1] - 1])) + { + ent_set_anim(acting_entity, animattacks[acting_entity->modeldata.atchain[acting_entity->modeldata.chainlength - 1] - 1], 0); + } + goto endthinkcheck; + } + acting_entity->stalltime = 0; + } + + /* + * Attack button. Handle basic attack + * button actions (attack, get, etc.). + */ + + if((acting_player->playkeys & FLAG_ATTACK) && notinair) + { + acting_player->playkeys &= ~FLAG_ATTACK; + acting_entity->stalltime = 0; // Disable the attack3 stalltime + + if((acting_entity->ducking & DUCK_ACTIVE) && validanim(acting_entity, ANI_DUCKATTACK) && PLAYER_MIN_Z == PLAYER_MAX_Z) //acting_player->keys & FLAG_MOVEDOWN + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + ent_set_anim(acting_entity, ANI_DUCKATTACK, 0); + goto endthinkcheck; + } + + if(acting_entity->running && validanim(acting_entity, ANI_RUNATTACK)) // New run attack code section + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + acting_entity->running = RUN_STATE_NONE; + ent_set_anim(acting_entity, ANI_RUNATTACK, 0); + goto endthinkcheck; + } + + /* + * Back attack. If player's command buffer matches + * the back attack sequence, we'll attempt to do + * a back attack. + */ + const key_mask_t sequence_back_attack[] = {FLAG_BACKWARD, FLAG_ATTACK}; + + if(validanim(acting_entity, ANI_ATTACKBACKWARD) && match_combo(sequence_back_attack, acting_player, 2)) { + const s_command_input_event* attack_input_event; + const s_command_input_event* backward_input_event; + + uint64_t history_index = + acting_player->command_input_index; + + uint64_t remaining_events = + acting_player->command_input_count; + + /* + * Get the last two press events. Reading through + * the compatibility helper prevents future release + * and timed hold events from disturbing back attack. + */ + attack_input_event = command_input_history_previous_press( + acting_player, + &history_index, + &remaining_events + ); + + backward_input_event = command_input_history_previous_press( + acting_player, + &history_index, + &remaining_events + ); + + /* + * Did back attack inputs come within the + * time window? If so, we'll do a back attack. + */ + if(attack_input_event + && backward_input_event + && attack_input_event->time + - backward_input_event->time + < global_config.game_speed / 10) { + + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = 0; + acting_entity->velocity.z = 0; + + if(acting_entity->direction == DIRECTION_LEFT + && (backward_input_event->press & FLAG_MOVELEFT)) { + + acting_entity->direction = DIRECTION_RIGHT; + + } else if(acting_entity->direction == DIRECTION_RIGHT + && (backward_input_event->press & FLAG_MOVERIGHT)) { + acting_entity->direction = DIRECTION_LEFT; + } + + acting_entity->combostep[0] = 0; + ent_set_anim(acting_entity, ANI_ATTACKBACKWARD, 0); + goto endthinkcheck; + } + } + + /* + * Get item. If player is standing on an item + * and the get animation is valid, then + * we'll pick it up and run the get animation. + */ + if(validanim(acting_entity, ANI_GET) + && (other = find_ent_here(acting_entity, acting_entity->position.x, acting_entity->position.z, TYPE_ITEM, player_test_pickable))) { + + acting_entity->velocity.x = acting_entity->velocity.z = 0; + set_getting(acting_entity); + acting_entity->takeaction = common_get; + ent_set_anim(acting_entity, ANI_GET, 0); + + /* + * Item "attacks" collector to make it + * easy to script actions on item pick up. + */ + do_item_script(acting_entity, other); + + didfind_item(other); + goto endthinkcheck; + } + + // Use stalltime to charge end-move + acting_entity->stalltime = _time; + acting_entity->velocity.x = acting_entity->velocity.z = 0; + + if( acting_entity->weapent && + acting_entity->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && + validanim(acting_entity, ANI_THROWATTACK) ) + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + ent_set_anim(acting_entity, ANI_THROWATTACK, 0); + goto endthinkcheck; + } + else if(perform_atchain()) + { + if(global_sample_list.punch >= 0 && acting_entity->attacking != ATTACKING_NONE) + { + sound_play_sample(global_sample_list.punch, 0, savedata.effectvol, savedata.effectvol, 100); + } + goto endthinkcheck; + } + + } + + /* + * Jumping contorl. + */ + + if(acting_player->playkeys & FLAG_JUMP && notinair) + { + // Added !inair(acting_entity) so players can't jump when falling into holes + acting_player->playkeys &= ~FLAG_JUMP; + + /* + * Z axis control. + * + * If player can initialize a Z jump + * and Up or Down keys are active we'll + * get a portion of their jump speed as + * a Z velocity. We'll use this value as + * the Z parameter for all downstream + * jump function calls. + */ + + if (acting_entity->modeldata.air_control & AIR_CONTROL_JUMP_Z_INITIAL) + { + if (acting_player->keys & FLAG_MOVEUP) + { + initial_jump_velocity_z = -acting_entity->modeldata.jumpspeed * 0.5; + } + else if (acting_player->keys & FLAG_MOVEDOWN) + { + initial_jump_velocity_z = acting_entity->modeldata.jumpspeed * 0.5; + } + } + else + { + initial_jump_velocity_z = 0.0; + } + + if(acting_entity->running) + { + //Slide + if((acting_player->keys & FLAG_MOVEDOWN) && validanim(acting_entity, ANI_RUNSLIDE)) + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + acting_entity->running = RUN_STATE_NONE; + ent_set_anim(acting_entity, ANI_RUNSLIDE, 0); + goto endthinkcheck; + } + + /* Jumping allowed? */ + if (!(acting_entity->modeldata.air_control & AIR_CONTROL_JUMP_DISABLE)) + { + if (validanim(acting_entity, ANI_RUNJUMP)) + { + tryjump(acting_entity->modeldata.runjumpheight, acting_entity->modeldata.jumpspeed * acting_entity->modeldata.runjumpdist, initial_jump_velocity_z, ANI_RUNJUMP); + } + else if (validanim(acting_entity, ANI_FORWARDJUMP)) + { + tryjump(acting_entity->modeldata.runjumpheight, acting_entity->modeldata.jumpspeed * acting_entity->modeldata.runjumpdist, initial_jump_velocity_z, ANI_FORWARDJUMP); + } + else if (validanim(acting_entity, ANI_JUMP)) + { + tryjump(acting_entity->modeldata.runjumpheight, acting_entity->modeldata.jumpspeed * acting_entity->modeldata.runjumpdist, initial_jump_velocity_z, ANI_JUMP); + } + } + } + else + { + //Slide + if((acting_player->keys & FLAG_MOVEDOWN) && validanim(acting_entity, ANI_SLIDE)) + { + acting_entity->takeaction = common_attack_proc; + set_attacking(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + acting_entity->combostep[0] = 0; + acting_entity->running = RUN_STATE_NONE; + ent_set_anim(acting_entity, ANI_SLIDE, 0); + goto endthinkcheck; + } + + /* Jumping allowed? */ + if (!(acting_entity->modeldata.air_control & AIR_CONTROL_JUMP_DISABLE)) + { + + /* + * Handle left/right direction command. + * + * If left or right key is active, + * then switch facing accordingly and + * continue on to moving jump logic. + * + * Otherwise perform a jump with no + * horizontal velocity. + */ + + if (!(acting_player->keys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)) && validanim(acting_entity, ANI_JUMP)) + { + tryjump(acting_entity->modeldata.jumpheight, 0, initial_jump_velocity_z, ANI_JUMP); + goto endthinkcheck; + } + else if ((acting_player->keys & FLAG_MOVELEFT)) + { + acting_entity->direction = DIRECTION_LEFT; + } + else if ((acting_player->keys & FLAG_MOVERIGHT)) + { + acting_entity->direction = DIRECTION_RIGHT; + } + + /* + * Horizontal moving jump. + */ + + if (validanim(acting_entity, ANI_FORWARDJUMP)) + { + tryjump(acting_entity->modeldata.jumpheight, acting_entity->modeldata.jumpspeed, initial_jump_velocity_z, ANI_FORWARDJUMP); + } + else if (validanim(acting_entity, ANI_JUMP)) + { + tryjump(acting_entity->modeldata.jumpheight, acting_entity->modeldata.jumpspeed, initial_jump_velocity_z, ANI_JUMP); + } + } + } + return; + } + + /* + * Run stop. + */ + run_try_runstop_player(acting_entity, acting_player); + + /* + */ + + if(PLAYER_MIN_Z != PLAYER_MAX_Z && acting_entity->ducking == DUCK_NONE) + { + // More of a platform feel + if(acting_player->keys & FLAG_MOVEUP) + { + if(validanim(acting_entity, ANI_UP) && acting_entity->running == RUN_STATE_NONE) + { + action = ACTION_UP; + acting_entity->velocity.z = -acting_entity->modeldata.speed.x / 2; // Used for up animation + } + else if(acting_entity->running & RUN_STATE_START_X) + { + action = ACTION_RUN; + + if (acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_RIGHT_DASH_FIXED + && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_UP_ENABLED)) { + acting_entity->velocity.z = 0.0; + } + else + { + acting_entity->velocity.z = -acting_entity->modeldata.runspeed / 2; // Moves up at a faster rate running + } + } + else + { + action = ACTION_WALK; + acting_entity->velocity.z = -acting_entity->modeldata.speed.x / 2; + } + } + else if(acting_player->keys & FLAG_MOVEDOWN) + { + if(validanim(acting_entity, ANI_DOWN) && acting_entity->running == RUN_STATE_NONE) + { + action = ACTION_DOWN; + acting_entity->velocity.z = acting_entity->modeldata.speed.x / 2; // Used for down animation + } + else if(acting_entity->running & RUN_STATE_START_X) + { + action = ACTION_RUN; + + if (acting_entity->modeldata.run_config_flags & RUN_CONFIG_X_RIGHT_DASH_FIXED + && !(acting_entity->modeldata.run_config_flags & RUN_CONFIG_Z_DOWN_ENABLED)) { + acting_entity->velocity.z = 0.0; + } + else + { + acting_entity->velocity.z = acting_entity->modeldata.runspeed / 2; // Moves up at a faster rate running + } + } + else + { + action = ACTION_WALK; + acting_entity->velocity.z = acting_entity->modeldata.speed.x / 2; + } + } + else if(!(acting_player->keys & (FLAG_MOVEUP | FLAG_MOVEDOWN))) + { + acting_entity->velocity.z = 0; + } + } + else if(acting_entity->ducking == DUCK_NONE && validanim(acting_entity, ANI_DUCK) && acting_player->keys & FLAG_MOVEDOWN && notinair) + { + tryduck(acting_entity); + goto endthinkcheck; + } + + if(acting_player->keys & FLAG_MOVELEFT && acting_entity->ducking == DUCK_NONE) + { + if(acting_entity->direction == DIRECTION_RIGHT) + { + //acting_entity->running = RUN_STATE_NONE; // Quits running if player changes direction + if(acting_entity->modeldata.turndelay && !acting_entity->turntime) + { + acting_entity->turntime = _time + acting_entity->modeldata.turndelay; + } + else if(acting_entity->turntime && _time >= acting_entity->turntime) + { + acting_entity->turntime = 0; + if(validanim(acting_entity, ANI_TURN)) + { + acting_entity->takeaction = common_turn; + set_turning(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_TURN, 0); + goto endthinkcheck; + } + acting_entity->direction = DIRECTION_LEFT; + } + else if(!acting_entity->modeldata.turndelay && validanim(acting_entity, ANI_TURN)) + { + acting_entity->takeaction = common_turn; + set_turning(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_TURN, 0); + goto endthinkcheck; + } + else if(!acting_entity->turntime) + { + acting_entity->direction = DIRECTION_LEFT; + } + } + else + { + acting_entity->turntime = 0; + } + + if(acting_entity->running) + { + action = ACTION_RUN; + acting_entity->velocity.x = -acting_entity->modeldata.runspeed; // If running, player moves at a faster rate + } + else if(action != ACTION_UP && action != ACTION_DOWN) + { + action = ACTION_WALK; + acting_entity->velocity.x = -acting_entity->modeldata.speed.x; + } + else + { + acting_entity->velocity.x = -acting_entity->modeldata.speed.x; + } + } + else if(acting_player->keys & FLAG_MOVERIGHT && acting_entity->ducking == DUCK_NONE) + { + if(acting_entity->direction == DIRECTION_LEFT) + { + //acting_entity->running = RUN_STATE_NONE; // Quits running if player changes direction + if(acting_entity->modeldata.turndelay && !acting_entity->turntime) + { + acting_entity->turntime = _time + acting_entity->modeldata.turndelay; + } + else if(acting_entity->turntime && _time >= acting_entity->turntime) + { + acting_entity->turntime = 0; + if(validanim(acting_entity, ANI_TURN)) + { + acting_entity->takeaction = common_turn; + set_turning(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_TURN, 0); + goto endthinkcheck; + } + acting_entity->direction = DIRECTION_RIGHT; + } + else if(!acting_entity->modeldata.turndelay && validanim(acting_entity, ANI_TURN)) + { + acting_entity->takeaction = common_turn; + set_turning(acting_entity); + acting_entity->velocity.x = acting_entity->velocity.z = 0; + ent_set_anim(acting_entity, ANI_TURN, 0); + goto endthinkcheck; + } + else if(!acting_entity->turntime) + { + acting_entity->direction = DIRECTION_RIGHT; + } + } + else + { + acting_entity->turntime = 0; + } + + if(acting_entity->running) + { + action = ACTION_RUN; + acting_entity->velocity.x = acting_entity->modeldata.runspeed; // If running, player moves at a faster rate + } + else if(action != ACTION_UP && action != ACTION_DOWN) + { + action = ACTION_WALK; + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + else + { + acting_entity->velocity.x = acting_entity->modeldata.speed.x; + } + } + else if(!((acting_player->keys & FLAG_MOVELEFT) || + (acting_player->keys & FLAG_MOVERIGHT)) ) + { + //acting_entity->running = RUN_STATE_NONE; // Player let go of left/right and so quits running + acting_entity->velocity.x = 0; + acting_entity->turntime = 0; + } + + if((other = find_ent_here(acting_entity, acting_entity->position.x, acting_entity->position.z, TYPE_ITEM, player_test_touch)) ) + { + do_item_script(acting_entity, other); + didfind_item(other); // Added function to clean code up a bit + } + + if (acting_entity->ducking & DUCK_ACTIVE) + { + if (!(acting_player->keys & FLAG_MOVEDOWN)) + { + tryduckrise(acting_entity); + goto endthinkcheck; + } + if(acting_player->keys & FLAG_MOVELEFT) + { + if(acting_entity->direction == DIRECTION_RIGHT) + { + acting_entity->direction = DIRECTION_LEFT; + } + } + else if(acting_player->keys & FLAG_MOVERIGHT) + { + if(acting_entity->direction == DIRECTION_LEFT) + { + acting_entity->direction = DIRECTION_RIGHT; + } + } + } + if (acting_entity->ducking) + { + goto endthinkcheck; + } + + + //White Dragon: prepare for idling animations... + if(action) + { + acting_entity->takeaction = NULL; + acting_entity->idling = IDLING_PREPARED; + } + + switch(action) + { + case ACTION_WALK: + // back walk feature + if(level && validanim(acting_entity, ANI_BACKWALK)) { + + if(acting_entity->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT) { + + back_walk = !acting_entity->direction; + + } else if(acting_entity->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_LEFT) { + + back_walk = acting_entity->direction; + + } else if((acting_entity->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT) && acting_entity->direction == DIRECTION_LEFT) { + + back_walk = true; + + } else if((acting_entity->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT) && acting_entity->direction == DIRECTION_RIGHT) { + + back_walk = true; + + } else if(acting_entity->turntime && acting_entity->modeldata.turndelay) { + + back_walk = true; + } + + if(back_walk) { + common_backwalk_anim(acting_entity); + } else { + common_walk_anim(acting_entity); + } + + } else { + common_walk_anim(acting_entity); + } + break; + case ACTION_UP: + common_up_anim(acting_entity); //ent_set_anim(acting_entity, ANI_UP, 0); // Set to up animation if exists + break; + case ACTION_DOWN: + common_down_anim(acting_entity); //ent_set_anim(acting_entity, ANI_DOWN, 0); // Set to down animation if exists + break; + case ACTION_RUN: + if (validanim(acting_entity, ANI_BACKRUN)) + { + if (is_in_backrun(acting_entity)) ent_set_anim(acting_entity, ANI_BACKRUN, 0); + else ent_set_anim(acting_entity, ANI_RUN, 0); + } + else ent_set_anim(acting_entity, ANI_RUN, 0); // Set to run animation if exists + + break; + default: + + if(acting_entity->idling) + { + common_idle_anim(acting_entity); + } + break; + } + + +endthinkcheck: + //insert check here + return; +} + +int is_in_backrun(entity* self) +{ + if ( ((self->modeldata.facing == FACING_ADJUST_RIGHT || level->facing == FACING_ADJUST_RIGHT) && self->velocity.x < 0) || + ((self->modeldata.facing == FACING_ADJUST_LEFT || level->facing == FACING_ADJUST_LEFT) && self->velocity.x > 0) || + (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_RIGHT)) && self->velocity.x > 0) || + (((self->modeldata.facing == FACING_ADJUST_LEVEL || level->facing == FACING_ADJUST_LEVEL) && (level->scrolldir & SCROLL_LEFT)) && self->velocity.x > 0) + ) return 1; + else return 0; +} + +//ammo count goes down +void subtract_shot() +{ + if(self->weapent && self->weapent->modeldata.weapon_properties.use_count) + { + self->weapent->modeldata.weapon_properties.use_count--; + + /* + * Out of uses? Drop the weapon. + */ + + if(!self->weapent->modeldata.weapon_properties.use_count) + { + self->weapent->modeldata.weapon_properties.loss_count = 0; + dropweapon(self, 0); + } + } +} + + +void dropweapon(entity* acting_entity, int flag) +{ + int wall = 0; + entity *other = NULL; + entity* weapon_entity = NULL; + s_weapon* weapon_properties = NULL; + + // If we already have a weapon, we'll need to discard it. + if(acting_entity->weapent) + { + /* + * Dump pointers to self's weapon entity and the + * weapon entity's modeldata weapon properties + * into local variables. This is just for eaiser + * reading downstream. + */ + + weapon_entity = acting_entity->weapent; + weapon_properties = &acting_entity->weapent->modeldata.weapon_properties; + + // 2019-09-29 - Not sure about this logic. It appears that only type shot + // weapons or weapons with shot ammo are dropped. Anything else is simply discarded. + // Need to evaluate all weapon logic to get the workflow. + if(weapon_properties->weapon_state & WEAPON_STATE_LIMITED_USE || (!(weapon_properties->weapon_state & WEAPON_STATE_LIMITED_USE) && weapon_properties->use_count)) + { + // If the flag is 2 or below, we subtract the flag's + // value from weapon counter. + if(flag < 2) + { + weapon_properties->loss_count -= flag; + } + + // We're going to use our own position for the weapon. + weapon_entity->direction = acting_entity->direction; + weapon_entity->position.z = acting_entity->position.z; + weapon_entity->position.x = acting_entity->position.x; + weapon_entity->position.y = acting_entity->position.y; + + // Get any walls and platforms. + other = check_platform(weapon_entity->position.x, weapon_entity->position.z, acting_entity); + wall = checkwall_index(weapon_entity->position.x, weapon_entity->position.z); + + // Place onto wall or platform. + if(other && other != weapon_entity) + { + weapon_entity->base += other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT]; + } + else if(wall >= 0) + { + weapon_entity->base += level->walls[wall].height; + } + + // Use the weapon's RESPAWN or SPAWN animations if available, otherwise + // go right to idle. + if(validanim(weapon_entity, ANI_RESPAWN)) + { + ent_set_anim(weapon_entity, ANI_RESPAWN, 1); + } + else if(validanim(weapon_entity, ANI_SPAWN)) + { + ent_set_anim(weapon_entity, ANI_SPAWN, 1); + } + else + { + if(validanim(weapon_entity, ANI_IDLE)) ent_set_anim(weapon_entity, ANI_IDLE, 1); + } + + // If the weapon's counter is depleted, then weapon is lost for good. + // If it is an "animal", then we apply the animal running away logic. + // Otherwise the weapon blinks out. + if(!weapon_properties->loss_count) + { + if(!(acting_entity->modeldata.weapon_properties.weapon_state & WEAPON_STATE_ANIMAL)) + { + weapon_entity->blink = 1; + weapon_entity->takeaction = common_lie; + } + else + { + weapon_entity->modeldata.type = TYPE_NONE; + weapon_entity->think = runanimal; + } + } + weapon_entity->nextthink = _time + 1; + } + + // Clear our tracking variable that keeps the weapon entity pointer. + acting_entity->weapent = NULL; + } + + // Flag 2 means we're probably setting the weapon directly (ex: setweapon command). + // In that case we don't worry about a weapon entity. Just switch ourselves over + // to the weapon model. + if(flag < 2) + { + if(acting_entity->modeldata.type & TYPE_PLAYER) + { + if(player[acting_entity->playerindex].weapnum) + { + set_weapon(acting_entity, player[acting_entity->playerindex].weapnum, 0); + } + else + { + set_weapon(acting_entity, level->setweap, 0); + } + } + else + { + set_weapon(acting_entity, 0, 0); + } + } + + // Model override. If this is populated, we use its value + // to locate a model by index and revert to that instead + // of the default model when a weapon is lost. + if(acting_entity->modeldata.weapon_properties.loss_index != MODEL_INDEX_NONE) + { + set_weapon(acting_entity, acting_entity->modeldata.weapon_properties.loss_index, 0); + } +} + + +int player_takedamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, int fall_flag, const s_defense* defense_object) +{ + s_attack atk = *attack_object; + //printf("damaged by: '%s' %d\n", other->name, attack->attack_force); + + // Kratus (10-2021) Now the "infinite health cheat" will check the damage source, it will avoid some "special" damage sources + + // Damage comes from a normal source? + const bool normal_damage = (!is_attack_type_special(atk.attack_type)) ? true : false; + + if((global_config.cheats & CHEAT_OPTIONS_HEALTH_ACTIVE && normal_damage) + || (level->nohurt == DAMAGE_FROM_ENEMY_OFF && (attacking_entity->modeldata.type & TYPE_ENEMY))) + { + atk.attack_force = 0; + } + + return common_takedamage(target_entity, attacking_entity, &atk, fall_flag, defense_object); +} + +//////////////////////////////// + +// Called when player re-enters the game. +// Drop all enemies EXCEPT for the linked/frozen ones. +void drop_all_enemies() +{ + int i; + entity *weapself = self; + s_attack attack = emptyattack; + + for(i = 0; i < ent_max; i++) + { + if(ent_list[i]->exists && + ent_list[i]->energy_state.health_current > 0 && + (ent_list[i]->modeldata.type & TYPE_ENEMY) && + !ent_list[i]->owner && // Don't want to knock down a projectile + !ent_list[i]->frozen && // Don't want to unfreeze a frozen enemy + !(ent_list[i]->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) && + !(ent_list[i]->modeldata.pain_config_flags & PAIN_CONFIG_FALL_DISABLE) && + validanim(ent_list[i], ANI_FALL) ) + { + ent_list[i]->attacking = ATTACKING_NONE; + ent_list[i]->projectile = BLAST_NONE; + ent_list[i]->takeaction = common_fall;//enemy_fall; + ent_list[i]->damage_on_landing.attack_force = 0; + ent_list[i]->damage_on_landing.attack_type = ATK_NONE; + self = ent_list[i]; + ent_unlink(self); + ent_list[i]->velocity.x = (self->direction == DIRECTION_RIGHT) ? (-1.2) : 1.2; + + if(ent_list[i]->modeldata.weapon_properties.loss_condition & WEAPON_LOSS_CONDITION_STAGE) + { + dropweapon(self, 1); + } + + toss(ent_list[i], 2.5 + randf(1)); + ent_list[i]->knockdowncount = ent_list[i]->modeldata.knockdowncount; + + ent_list[i]->knockdowntime = 0; + + set_fall(ent_list[i], self, &attack, 1); + } + } + self = weapself; +} + + + +/* Called when boss dies. */ +void kill_all_enemies() +{ + int i; + s_attack attack = emptyattack; + entity *tmpself = NULL; + + attack.attack_type = ATK_BOSS_DEATH; + attack.dropv = default_model_dropv; + + /* + * Downstream damage functions use + * self global, so we populate it with + * entity cursor in each iteration of + * loop below. Keep current value here + * so we can restore when loop is finished. + */ + tmpself = self; + + /* + * Every valid enemy type with a takedamage + * function takes its current health in damage. + */ + for(i = 0; i < ent_max; i++) + { + if( ent_list[i]->exists + && ent_list[i]->energy_state.health_current > 0 + && (ent_list[i]->modeldata.type & TYPE_ENEMY) + && ent_list[i]->takedamage) + { + self = ent_list[i]; + + attack.attack_force = self->energy_state.health_current; + self->takedamage(self, self, &attack, 0, self->defense); + } + } + + self = tmpself; +} + + + +void smart_bomb(entity *e, s_attack *attack) { // New method for smartbombs + + int i, hit = 0; + entity *tmpself = NULL; + + tmpself = self; + for(i = 0; i < ent_max; i++) { + + if( ent_list[i]->exists + && ent_list[i] != e + && ent_list[i]->energy_state.health_current > 0 + && faction_check_is_hostile(e, ent_list[i])) { + + self = ent_list[i]; + hit = 1; // for nocost, if the bomb doesn't hit, it won't cost energy + + lasthit.attack = attack; + lasthit.attacker = e; + lasthit.collision_attack = NULL; + lasthit.confirm = 1; + lasthit.detect_body = NULL; + lasthit.detect_collision_attack = NULL; + lasthit.detect_collision_body = NULL; + lasthit.position.x = self->position.x; + lasthit.position.y = self->position.y; + lasthit.position.z = self->position.z; + lasthit.target = self; + + if(self->takedamage) { + + const s_defense* defense_object = defense_find_current_object(self, NULL, attack->attack_type); + + //attack.attack_drop = self->modeldata.knockdowncount+1; + self->takedamage(self, e, attack, 0, defense_object); + + } else { + self->energy_state.health_current -= attack->attack_force; + + if(self->energy_state.health_current <= 0) { + kill_entity(self, KILL_ENTITY_TRIGGER_SMARTBOMB); + } + } + + spawn_attack_flash(self, attack, attack->flash.model_hit, self->modeldata.flash.model_block); + + } + } + + if(nocost && hit && smartbomber) { // don't use e, because this can be an item-bomb + self = smartbomber; + + if(check_energy(ENERGY_TYPE_MP, ANI_SPECIAL)) { + self->energy_state.mp_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } else { + self->energy_state.health_current -= self->modeldata.animation[ANI_SPECIAL]->energy_cost.cost; + } + + } + + self = tmpself; + +} + + +//////////////////////////////// + +void anything_walk() +{ + if(self->position.x < advancex - 80 || self->position.x > advancex + (videomodes.hRes + 80)) + { + kill_entity(self, KILL_ENTITY_TRIGGER_WALK_OUT_OF_BOUNDS); + return; + } + //self->position.x += self->velocity.x; +} + +// Caskey, Damon V. +// 2019-12-18 +// +// Apply color set adjustment to entity, possibly +// based on a parent/owner depending on color set +// adjustment setting. +void apply_color_set_adjust(entity* ent, entity* parent, e_color_adjust adjustment) +{ + int i = 0; // Loop cursor. + + // Apply color setting. + switch (adjustment) + { + default: + + // Use adjustment value as color set index. + ent_set_colourmap(ent, adjustment); + break; + + case COLORSET_ADJUST_NONE: + + // Do nothing. + break; + + case COLORSET_ADJUST_PARENT_INDEX: + + // Locate parent's current color set index. Then + // set our color set by that index. + + for (i = 0; i < parent->modeldata.maps_loaded; i++) + { + if (parent->colourmap == parent->modeldata.colourmap[i]) + { + ent_set_colourmap(ent, i); + break; + } + } + break; + + case COLORSET_ADJUST_PARENT_TABLE: + + // Use parent's color table. + + ent->colourmap = parent->colourmap; + break; + + } +} + +/* +* Caskey, Damon V. +* +* 2019-12-22 +* Copy the faction settings from a +* source entity. +*/ +void faction_copy_all(entity* dest, entity* source) +{ + faction_copy_data(&dest->modeldata.faction, &source->faction); + faction_copy_data(&dest->faction, &source->faction); +} + +/* +* Caskey, Damon V. +* 2023-02-28 +* +* Copy faction properties if +* conditions pass. +*/ +void faction_copy_data(s_faction* dest, s_faction* source) { + + /* + * The destination is always a faction + * object, regardless of which faction + * properties qualify for copying. + */ + dest->object_type = OBJECT_TYPE_FACTION; + + /* + * Faction groups. Only copy if source or + * destination has a value and does not + * have the no copy flag. + * + * I'd rather be able to copy empty values + * as well, but that requires inserting + * default values in several differnt + * locations around the code. Better to + * keep it here and maintainable. + */ + + if (source->damage_direct != FACTION_GROUP_NONE + && !(source->damage_direct & FACTION_GROUP_NO_COPY) + && !(dest->damage_direct & FACTION_GROUP_NO_COPY)) { + dest->damage_direct = source->damage_direct; + } + + if (source->damage_indirect != FACTION_GROUP_NONE + && !(source->damage_indirect & FACTION_GROUP_NO_COPY) + && !(dest->damage_indirect & FACTION_GROUP_NO_COPY)) { + dest->damage_indirect = source->damage_indirect; + } + + if (source->hostile != FACTION_GROUP_NONE + && !(source->hostile & FACTION_GROUP_NO_COPY) + && !(dest->hostile & FACTION_GROUP_NO_COPY)) { + dest->hostile = source->hostile; + } + + if (source->member != FACTION_GROUP_NONE + && !(source->member & FACTION_GROUP_NO_COPY) + && !(dest->member & FACTION_GROUP_NO_COPY)) { + dest->member = source->member; + } + + /* + * Types. Same rule as faction groups. + */ + + if (source->type_damage_direct != TYPE_UNDECLARED + && !(source->type_damage_direct & TYPE_NO_COPY) + && !(dest->type_damage_direct & TYPE_NO_COPY)) { + + dest->type_damage_direct = source->type_damage_direct; + } + + if (source->type_damage_indirect != TYPE_UNDECLARED + && !(source->type_damage_indirect & TYPE_NO_COPY) + && !(dest->type_damage_indirect & TYPE_NO_COPY)) { + dest->type_damage_indirect = source->type_damage_indirect; + } + + if (source->type_hostile != TYPE_UNDECLARED + && !(source->type_hostile & TYPE_NO_COPY) + && !(dest->type_hostile & TYPE_NO_COPY)) { + dest->type_hostile = source->type_hostile; + } +} + +/* +* Caskey, Damon V. +* 2022-05-24 +* +* Read a text argument for model copy flag +* and output appropriate constant. +*/ +faction_group_mask_t faction_get_flag_from_string(const char* value) { + + const struct { + const char* text_name; + faction_group_mask_t flag; + + } flag_lookup_table[] = { + { "none", FACTION_GROUP_NONE }, + { "all", FACTION_GROUP_ALL_NORMAL }, + {"all0", FACTION_GROUP_ALL_NORMAL_0 }, + {"all1", FACTION_GROUP_ALL_NORMAL_1 }, + { "neutral", FACTION_GROUP_NEUTRAL }, + { "no_copy", FACTION_GROUP_NO_COPY }, + { "player_verses", FACTION_GROUP_PLAYER_VERSES }, + { "type_exclusive", FACTION_GROUP_TYPE_EXCLUSIVE }, + { "type_inclusive", FACTION_GROUP_TYPE_INCLUSIVE }, + { "a", FACTION_GROUP_A }, + { "b", FACTION_GROUP_B }, + { "c", FACTION_GROUP_C }, + { "d", FACTION_GROUP_D }, + { "e", FACTION_GROUP_E }, + { "f", FACTION_GROUP_F }, + { "g", FACTION_GROUP_G }, + { "h", FACTION_GROUP_H }, + { "i", FACTION_GROUP_I }, + { "j", FACTION_GROUP_J }, + { "k", FACTION_GROUP_K }, + { "l", FACTION_GROUP_L }, + { "m", FACTION_GROUP_M }, + { "n", FACTION_GROUP_N }, + { "o", FACTION_GROUP_O }, + { "p", FACTION_GROUP_P }, + { "q", FACTION_GROUP_Q }, + { "r", FACTION_GROUP_R }, + { "s", FACTION_GROUP_S }, + { "t", FACTION_GROUP_T }, + { "u", FACTION_GROUP_U }, + { "v", FACTION_GROUP_V }, + { "w", FACTION_GROUP_W }, + { "x", FACTION_GROUP_X }, + { "y", FACTION_GROUP_Y }, + { "z", FACTION_GROUP_Z }, + { "a1", FACTION_GROUP_A1 }, + { "b1", FACTION_GROUP_B1 }, + { "c1", FACTION_GROUP_C1 }, + { "d1", FACTION_GROUP_D1 }, + { "e1", FACTION_GROUP_E1 }, + { "f1", FACTION_GROUP_F1 }, + { "g1", FACTION_GROUP_G1 }, + { "h1", FACTION_GROUP_H1 }, + { "i1", FACTION_GROUP_I1 }, + { "j1", FACTION_GROUP_J1 }, + { "k1", FACTION_GROUP_K1 }, + { "l1", FACTION_GROUP_L1 }, + { "m1", FACTION_GROUP_M1 }, + { "n1", FACTION_GROUP_N1 }, + { "o1", FACTION_GROUP_O1 }, + { "p1", FACTION_GROUP_P1 }, + { "q1", FACTION_GROUP_Q1 }, + { "r1", FACTION_GROUP_R1 }, + { "s1", FACTION_GROUP_S1 }, + { "t1", FACTION_GROUP_T1 }, + { "u1", FACTION_GROUP_U1 }, + { "v1", FACTION_GROUP_V1 }, + { "w1", FACTION_GROUP_W1 }, + { "x1", FACTION_GROUP_X1 }, + { "y1", FACTION_GROUP_Y1 }, + { "z1", FACTION_GROUP_Z1 } + }; + + const size_t list_count = sizeof(flag_lookup_table) / sizeof(*flag_lookup_table); + + for (size_t i = 0; i < list_count; i++) { + if (stricmp(value, flag_lookup_table[i].text_name) == 0) { + return flag_lookup_table[i].flag; + } + } + + /* + * Couldn't find a match in the lookup + * table. Send alert to log and return + * none flag. + */ + + printf("\n\n Unknown faction (%s). \n", value); + return FACTION_GROUP_NONE; +} + +/* +- Caskey, Damon V. +- 2026-08-11 +- +- Read faction arguments directly from the source line and + combine their corresponding group flags. +*/ +faction_group_mask_t faction_get_flags_from_command_line( + const char* command_line +) +{ + const char* value; + s_command_argument_reader reader; + faction_group_mask_t result = FACTION_GROUP_NONE; + + command_argument_reader_initialize(&reader, command_line, 1); + + while(command_argument_reader_next(&reader, &value)) { + result |= faction_get_flag_from_string(value); + } + + return result; +} + +/* +* Caskey, Damon V. +* 2023-02-26 +* +* Return true if acting entity can +* hit target entity with attacks. +*/ +bool faction_check_can_damage(entity* acting_entity, entity* target_entity, const bool indirect) { + + e_entity_type acting_type; + e_entity_type target_type; + faction_group_mask_t acting_faction; + faction_group_mask_t acting_faction_filtered; + faction_group_mask_t target_faction; + + if (!acting_entity || !target_entity) { + return false; + } + + /* + * We will use different acting faction property + * and type if the indirect flag is set. + */ + + if (indirect) { + acting_faction = acting_entity->faction.damage_indirect; + acting_type = acting_entity->faction.type_damage_indirect; + + } else { + acting_faction = acting_entity->faction.damage_direct; + acting_type = acting_entity->faction.type_damage_direct; + } + + acting_faction_filtered = acting_faction; + + /* + * Remove special types. + */ + + acting_faction_filtered &= ~FACTION_GROUP_NO_CHECK; + acting_type &= ~TYPE_NO_CHECK; + + /* + * Check player interaction. + */ + + if (faction_check_player_verses(acting_entity, target_entity, acting_faction)) { + return false; + } + + target_type = target_entity->modeldata.type; + + /* + * If one of the acting factions is the + * type exclusive group, then we only + * check acting types vs. target type + * and ignore other factions. + */ + + if (acting_faction & FACTION_GROUP_TYPE_EXCLUSIVE) { + + if (acting_type & target_type) { + return true; + } else { + return false; + } + } + + /* + * Now we compare our acting faction(s) to the + * faction(s) target is a member of for any match. + */ + + target_faction = target_entity->faction.member; + + if (acting_faction_filtered & target_faction) { + /* + * If one of the acting factions is + * the tye inclusing group, then we + * also check acting types vs the + * target's type. + */ + + if (acting_faction & FACTION_GROUP_TYPE_INCLUSIVE) { + + if (acting_type & target_type) { + return true; + + } else { + return false; + } + } + + return true; + } + + return false; +} + +/* +* Caskey, Damon V. +* 2023-02-26 +* +* Return true if acting entity is +* hostile toward target entity. +*/ +int faction_check_is_hostile(entity* acting_entity, entity* target_entity) +{ + //printf("\n\n faction_check_is_hostile(%p, %p)", acting_entity, target_entity); + + e_entity_type acting_type; + e_entity_type target_type; + faction_group_mask_t acting_faction; + faction_group_mask_t filtered_faction; + faction_group_mask_t target_faction; + + if (!acting_entity || !target_entity) + { + return 0; + } + + //printf("\n\t acting_entity->name: %s, target_entity->name: %s", acting_entity->name, target_entity->name); + + acting_faction = acting_entity->faction.hostile; + filtered_faction = acting_faction; + acting_type = acting_entity->faction.type_hostile; + target_type = target_entity->modeldata.type; + + //printf("\n\t acting_faction: %d", acting_faction); + //printf("\n\t acting_type: %d", acting_type); + //printf("\n\t target_type: %d", target_type); + + /* + * Remove special types. + */ + + filtered_faction &= ~FACTION_GROUP_NO_CHECK; + acting_type &= ~TYPE_NO_CHECK; + + //printf("\n\t acting_faction: %d", acting_faction); + //printf("\n\t acting_type: %d", acting_type); + + /* + * Check player interaction. + */ + + if (faction_check_player_verses(acting_entity, target_entity, acting_faction)) + { + //printf("\n\t PLayer Vs. Return 0"); + + return 0; + } + + /* + * If one of the acting factions is the + * type exclusive group, then we only + * check acting types vs. target type + * and ignore other factions. + */ + + if (acting_faction & FACTION_GROUP_TYPE_EXCLUSIVE) + { + //printf("\n\t\t acting_faction & FACTION_GROUP_TYPE_EXCLUSIVE: %d", acting_faction & FACTION_GROUP_TYPE_EXCLUSIVE); + + if (acting_type & target_type) + { + //printf("\n\t\t acting_type & target_type: %d (yes)", acting_type & target_type); + + return 1; + } + else + { + printf("\n\t\t acting_type & target_type: %d (no)", acting_type & target_type); + + return 0; + } + } + + /* + * Now we compare our acting faction(s) to the + * faction(s) target is a member of for any match. + */ + + target_faction = target_entity->faction.member; + + if (filtered_faction & target_faction) + { + /* + * If one of the acting factions is + * the tye inclusing group, then we + * also check acting types vs the + * target's type. + */ + + if (acting_faction & FACTION_GROUP_TYPE_INCLUSIVE) + { + //printf("\n\t\t acting_faction & FACTION_GROUP_TYPE_INCLUSIVE: %d", acting_faction & FACTION_GROUP_TYPE_INCLUSIVE); + + if (acting_type & target_type) + { + //printf("\n\t\t acting_type & target_type: %d (yes)", acting_type & target_type); + + return 1; + } + else + { + //printf("\n\t\t acting_type & target_type: %d (no)", acting_type & target_type); + + return 0; + } + } + + return 1; + } + + //printf("\n\t No match."); + + return 0; +} + +/* +* Caskey, Damon V. +* 2023-02-28 +* +* Returs 1 if players are not allowed +* vs. interaction on a given faction +* property. Ex. Hostile, direct damage, +* indirect damage. +*/ +int faction_check_player_verses(entity* acting_entity, entity* target_entity, faction_group_mask_t faction_property) +{ + if (!acting_entity || !target_entity) + { + return 0; + } + + /* + * Both entites must be players. + */ + + if (!(acting_entity->modeldata.type & TYPE_PLAYER)) + { + return 0; + } + + if (!(target_entity->modeldata.type & TYPE_PLAYER)) + { + return 0; + } + + /* + * In faction group to override friendly + * fire settings? + */ + + if (faction_property & FACTION_GROUP_PLAYER_VERSES) + { + return 0; + } + + /* + * If VS. mode turned off in options + * or level nohit is enabled, then + * we are "friendly" toward target. + */ + + if (savedata.mode) + { + return 1; + } + + if (level && level->nohit == DAMAGE_FROM_PLAYER_OFF) + { + return 1; + } + + return 0; +} + +/* +* Caskey, Damon V. +* 2019-12-18 (refactor) +* +* Original author unknown (Tails?). Refactored to remove the ever-growing parameter list +* and consolidate projectile spawn logic. Spawns an entity and fires it as a projectile. +* Model used for spawn is determined by a hierarchy of legacy parameters (see detailed +* comments in function). +* +* Returns pointer of spawned projectile, or NULL on fail. +*/ +entity *knife_spawn(entity *parent, s_projectile *projectile) +{ + entity *projectile_entity = NULL; + + s_axis_principal_float position; + e_direction direction = DIRECTION_RIGHT; + e_projectile_prime projectile_prime = PROJECTILE_PRIME_NONE; + + /* If there's no projectile or parent setting, exit now. */ + if (!projectile || !parent) + { + return NULL; + } + + position.x = parent->position.x; + position.y = parent->position.y + projectile->position.y; + position.z = parent->position.z + projectile->position.z; + + /* + * Now we need to spawn the projectile entity. There are many haphazard legacy + * additions to sift through, so we need to prioritize which model to spawn. + * In general, we work back from most granular to most global. + * + * From highest to lowest priority: + * + * 1. Projectile Knife property. + * 2. Projectile Flash property. + * 3. Using weapon with model Project property. + * 4. Model Knife property. + * 5. Model Pshotno property. + * 6. Global hardcode model name, "Knife". + * 7. Global hardcode model name, "Shot". + */ + if (projectile->knife >= 0) + { + projectile_entity = spawn(position.x, position.z, position.y, direction, NULL, projectile->knife, NULL); + + projectile_prime |= PROJECTILE_PRIME_BASE_Y; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + projectile_prime |= PROJECTILE_PRIME_SOURCE_PROJ_KNIFE; + } + else if (projectile->flash >= 0) + { + projectile_entity = spawn(position.x, position.z, position.y, direction, NULL, projectile->flash, NULL); + + projectile_prime |= PROJECTILE_PRIME_BASE_FLOOR; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_STATIONARY; + projectile_prime |= PROJECTILE_PRIME_SOURCE_PROJ_FLASH; + } + else if (parent->weapent && parent->weapent->modeldata.project >= 0) + { + projectile_entity = spawn(position.x, position.z, position.y, direction, NULL, parent->weapent->modeldata.project, NULL); + + projectile_prime |= PROJECTILE_PRIME_BASE_Y; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + projectile_prime |= PROJECTILE_PRIME_SOURCE_WEAPON_PROJECTILE; + } + else if (parent->modeldata.knife >= 0) + { + projectile_entity = spawn(position.x, position.z, position.y, direction, NULL, parent->modeldata.knife, NULL); + + projectile_prime |= PROJECTILE_PRIME_BASE_Y; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + projectile_prime |= PROJECTILE_PRIME_SOURCE_MODEL_KNIFE; + } + else if (parent->modeldata.pshotno >= 0) + { + projectile_entity = spawn(position.x, position.z, position.y, direction, NULL, parent->modeldata.pshotno, NULL); + + projectile_prime |= PROJECTILE_PRIME_BASE_FLOOR; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_STATIONARY; + projectile_prime |= PROJECTILE_PRIME_SOURCE_MODEL_PSHOTNO; + } + else + { + /* + * No model indexes set, so let's fall back to + * the legacy hardcode model names. + * + * Try hardcode "knife" first. If that fails, we'll try + * "shot" next. + */ + projectile_entity = spawn(position.x, position.z, position.y, direction, "Knife", MODEL_INDEX_NONE, NULL); + + if (projectile_entity) + { + /* + * Hardcode knife spawn successful. Mark as legacy knife + * and continue. + */ + projectile_prime |= PROJECTILE_PRIME_SOURCE_GLOBAL_KNIFE; + } + else + { + /* Try "shot". */ + projectile_entity = spawn(position.x, position.z, position.y, direction, "Shot", MODEL_INDEX_NONE, NULL); + + projectile_prime |= PROJECTILE_PRIME_SOURCE_GLOBAL_SHOT; + } + + projectile_prime |= PROJECTILE_PRIME_BASE_Y; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + } + + /* If we never successfully spawned a projectile entity, exit. */ + if(!projectile_entity) + { + return NULL; + } + + /* + * Get result of direction adjustment. We need this before we can handle + * positioning on X axis. + */ + direction = direction_get_adjustment_result(projectile_entity, parent, projectile->direction_adjust); + + /* + * Let's set up the spawn position. Reverse X when + * parent faces left. + * + * Apply default X position if creator did not give + * us a value. + */ + + if (projectile->position.x == PROJECTILE_LEGACY_COMPATABILITY_POSITION_X) + { + projectile->position.x = PROJECTILE_DEFAULT_POSITION_X; + } + + if (direction == DIRECTION_RIGHT) + { + position.x = parent->position.x + projectile->position.x; + } + else + { + position.x = parent->position.x - projectile->position.x; + } + + projectile_entity->direction = direction; + projectile_entity->position.x = position.x; + + /* + * Player projectiles are always type "shot", unless + * using the current PROJECTILE type. + */ + if (!(projectile_entity->modeldata.type & TYPE_PROJECTILE)) + { + if (parent->modeldata.type & TYPE_PLAYER) + { + projectile_entity->modeldata.type = TYPE_SHOT; + } + else + { + projectile_entity->modeldata.type = parent->modeldata.type; + } + } + + /* Apply projectile prime flags. */ + projectile_entity->projectile_prime = projectile_prime; + + /* + * Copy offense values from parent offense settings + * to projectile enity if requested. + */ + if (projectile->offense == PROJECTILE_OFFENSE_PARENT) + { + memcpy(projectile_entity->offense, parent->offense, sizeof(*projectile_entity->offense) * max_attack_types); + } + + /* Apply color adjustment. */ + apply_color_set_adjust(projectile_entity, parent, projectile->color_set_adjust); + + + /* + * If no move, then all speed is 0. Otherwise check for use of + * projectile velocity. If player supplied any value other + * than MODEL_SPEED_NONE, we use player's value. If not, fall + * back to default values. This is a bit overcomplicated, but + * allows players to supply a 0 velocity value on any axis. + */ + if (projectile_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + projectile_entity->modeldata.speed.x = 0; + projectile_entity->modeldata.speed.y = 0; + projectile_entity->modeldata.speed.z = 0; + } + else + { + /* Copy speed values from animation projectile settings to model. */ + projectile->velocity = projectile_entity->modeldata.speed; + } + + /* Set up behavior flags. */ + projectile_entity->spawntype = SPAWN_TYPE_PROJECTILE_NORMAL; + projectile_entity->owner = parent; + projectile_entity->nograb = 1; + projectile_entity->attacking = ATTACKING_ACTIVE; + projectile_entity->think = common_think; + projectile_entity->nextthink = _time + 1; + projectile_entity->trymove = NULL; + projectile_entity->takedamage = arrow_takedamage; + projectile_entity->takeaction = NULL; + projectile_entity->modeldata.aimove = AIMOVE1_ARROW; + projectile_entity->speedmul = 2; + projectile_entity->modeldata.aiattack = AIATTACK1_NOATTACK; + + if(!projectile_entity->modeldata.offscreenkill) + { + projectile_entity->modeldata.offscreenkill = 200; //default value + } + + /* Kill self when we hit. */ + if (projectile_entity->modeldata.remove_config & REMOVE_CONFIG_HIT) + { + projectile_entity->autokill |= AUTOKILL_ATTACK_HIT; + } + + /* Kill self when we finish animation. */ + if (projectile_entity->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + projectile_entity->autokill |= AUTOKILL_ANIMATION_COMPLETE; + } + + /* Is this a floor or flying projectile ? Set base accordingly. */ + if(projectile_entity->projectile_prime & PROJECTILE_PRIME_BASE_FLOOR) + { + projectile_entity->base = 0; + } + else + { + projectile_entity->base = position.y; + } + + /* + * Copy faction data from parent. + */ + + faction_copy_all(projectile_entity, parent); + + /* + * If player damage turned off, remove player type from + * hostile (so homing projectiles leave players alone) and + * from candamage. + */ + if((parent->modeldata.type & TYPE_PLAYER) && ((level && level->nohit == DAMAGE_FROM_PLAYER_OFF) || savedata.mode)) + { + projectile_entity->faction.type_hostile &= ~TYPE_PLAYER; + projectile_entity->faction.type_damage_direct &= ~TYPE_PLAYER; + } + + /* + * Set terrain and movement behavior flags. Legacy + * behavior wouldn't allow movement along Y axis + * without an arc, but we can't fix this without + * breaking compatability, so we'll need a different + * setup depending on what function creator used. + */ + + projectile_entity->modeldata.move_config_flags |= (MOVE_CONFIG_PROJECTILE_BASE_DIE | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL | MOVE_CONFIG_PROJECTILE_WALL_BOUNCE); + projectile_entity->modeldata.move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + + if (projectile_entity->projectile_prime & PROJECTILE_PRIME_INITIALIZE_LEGACY_PROJECTILE_FUNCTION) + { + projectile_entity->modeldata.move_config_flags |= MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + else + { + projectile_entity->modeldata.move_config_flags &= ~MOVE_CONFIG_SUBJECT_TO_GRAVITY; + } + + /* Execute the projectile's on spawn event. */ + execute_onspawn_script(projectile_entity); + + return projectile_entity; +} + +void bomb_explode() +{ + if(self->animating) + { + return; + } + kill_entity(self, KILL_ENTITY_TRIGGER_BOMB_EXPLODE_ANIMATION_COMPLETE); +} + +/* +* Caskey, Damon V. +* 2019-12-22 (refactor) +* +* Original author unknown (Tails?). Refactored to remove the ever-growing parameter list +* and consolidate projectile spawn logic. Spawns an entity and fires it as a bomb projectile. +* Model used for spawn is determined by a hierarchy of legacy parameters (see detailed +* comments in function). +* +* Returns pointer of spawned projectile, or NULL on fail. +*/ +entity *bomb_spawn(entity *parent, s_projectile *projectile) +{ + entity* ent = NULL; + s_axis_principal_float position; + e_direction direction = DIRECTION_RIGHT; + e_projectile_prime projectile_prime = PROJECTILE_PRIME_NONE; + + // If there's no projectile or parent setting, exit now. + if (!projectile || !parent) + { + return NULL; + } + + position.x = parent->position.x; + position.y = parent->position.y + projectile->position.y; + position.z = parent->position.z + projectile->position.z; + + /* + * Now we need to spawn the projectile entity. There are many haphazard legacy + * additions to sift through, so we need to prioritize which model to spawn. + * In general, we work back from most granular to most global. + * + * From highest to lowest priority: + * + * 1. Projectile Bomb property. + * 2. Using weapon with model bomb property. + * 3. Model bomb property. + */ + + if(projectile->bomb >= 0) + { + ent = spawn(position.x, position.z, position.y, direction, NULL, projectile->bomb, NULL); + + projectile_prime |= PROJECTILE_PRIME_SOURCE_PROJ_BOMB; + } + else if (self->weapent && self->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && self->weapent->modeldata.project >= 0) + { + ent = spawn(position.x, position.z, position.y, direction, NULL, self->weapent->modeldata.project, NULL); + + projectile_prime |= PROJECTILE_PRIME_SOURCE_WEAPON_PROJECTILE; + } + else if(self->modeldata.bomb >= 0) + { + ent = spawn(position.x, position.z, position.y, direction, NULL, self->modeldata.bomb, NULL); + + projectile_prime |= PROJECTILE_PRIME_SOURCE_MODEL_BOMB; + } + + projectile_prime |= PROJECTILE_PRIME_BASE_FLOOR; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + + // If we never successfully spawned a projectile entity, exit. + if(!ent) + { + return NULL; + } + + /* + * Get result of direction adjustment. We need this before we can handle + * positioning on X axis. + */ + direction = direction_get_adjustment_result(ent, parent, projectile->direction_adjust); + + /* + * Let's set up the spawn position. Reverse X when + * parent faces left. + * + * Apply default X position if creator did not give + * us a value. + */ + + if (projectile->position.x == PROJECTILE_LEGACY_COMPATABILITY_POSITION_X) + { + projectile->position.x = PROJECTILE_DEFAULT_POSITION_X; + } + + if (direction == DIRECTION_RIGHT) + { + position.x = parent->position.x + projectile->position.x; + } + else + { + position.x = parent->position.x - projectile->position.x; + } + + ent->direction = direction; + ent->position.x = position.x; + + + ent->projectile_prime = projectile_prime; + + /* + * If no move, then all speed is 0. Otherwise check for use of + * projectile velocity. If player supplied any value other + * than MODEL_SPEED_NONE, we use player's value. If not, fall + * back to default values. This is a bit overcomplicated, but + * allows players to supply a 0 velocity value on any axis. + */ + + if (ent->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + ent->modeldata.speed.x = 0; + ent->modeldata.speed.y = 0; + ent->modeldata.speed.z = 0; + } + else + { + // Copy speed values from animation projectile settings to model. + projectile->velocity.x = ent->modeldata.speed.x; + } + + /* + * Toss the bomb entity in an arc. + * + * We want to handle legacy behavior (use projectile jumpheight + * for Y toss velocity), allow author to apply a 0 value for Y + * velocity, and use the same velocity structure members for bomb + * and knife projectiles. The last part is an extra challenge because + * the default velocity needs for knife and bomb are not compatible. + * To handle all of this this we will set the velocity.y member to + * MODEL_SPEED_NONE specfically when a bomb projectile is requested + * by the Throwframe command or "bomb" type from legacy script function + * projectile(). If the author does not modify this value, we know + * to fall back to legacy behavior and use the projectile entity's + * model jumpheight. + * + * If the value is anything other than MODEL_SPEED_NONE, then we know + * the author requested a specific velocity (including 0) and will + * use author value instead of model jumpheight. + */ + + if (projectile->velocity.y == MODEL_SPEED_NONE) + { + toss(ent, ent->modeldata.jumpheight); + } + else + { + toss(ent, projectile->velocity.y); + } + + /* Apply color adjustment. */ + apply_color_set_adjust(ent, parent, projectile->color_set_adjust); + + ent->spawntype = SPAWN_TYPE_PROJECTILE_BOMB; + ent->attacking = ATTACKING_ACTIVE; + ent->owner = parent; + ent->nograb = 1; + ent->toexplode |= (EXPLODE_PREPARE_TOUCH | EXPLODE_PREPARE_GROUND); + + ent->think = common_think; + ent->nextthink = _time + 1; + ent->trymove = NULL; + ent->takeaction = NULL; + ent->modeldata.aimove = AIMOVE1_BOMB; + ent->modeldata.aiattack = AIATTACK1_NOATTACK; + ent->takedamage = common_takedamage; + ent->autokill &= ~AUTOKILL_ATTACK_HIT; + + if (ent->modeldata.move_config_flags & MOVE_CONFIG_NO_MOVE) + { + ent->autokill |= AUTOKILL_ANIMATION_COMPLETE; + } + + ent->speedmul = 2; + + /* + * Copy faction data. + */ + + faction_copy_all(ent, parent); + + ent->modeldata.move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + ent->modeldata.move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_PROJECTILE_BASE_DIE | MOVE_CONFIG_PROJECTILE_WALL_BOUNCE | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL); + + // Execute the projectile's on spawn event. + execute_onspawn_script(ent); + + return ent; +} + +// Spawn 3 stars +// +// Caskey, Damon V. +// 2019-12-17 +// +// Spawn three "star" projectiles. Meant for Eiji enemies in +// original Beats of Rage, who would jump and throw three star +// shuriken diagonally downward at players. Original author +// Roel, but modified several times by unknown parties. Refactored +// by DC 2019-12-17 to work with current projectile system. +// Obviously, we need this for backward compatibility with legacy +// modules, but otherwise script is the best choice to handle +// multiple projectiles or any other sort of specialized +// projectile spawns. +// +// Return TRUE if stars spawned, FALSE on fail. +int star_spawn(entity *parent, s_projectile *projectile) +{ + const int MAX_STARS = 3; + + entity *ent = NULL; + int i = 0; + int index = MODEL_INDEX_NONE; + int first_sortid = 0; + + s_axis_principal_float position; + e_direction direction = DIRECTION_RIGHT; + e_projectile_prime projectile_prime = PROJECTILE_PRIME_NONE; + + // If there's no projectile or parent setting, exit now. + if (!projectile || !parent) + { + return FALSE; + } + + position.x = parent->position.x; + position.y = parent->position.y + projectile->position.y; + position.z = parent->position.z + projectile->position.z; + + // Same concept as knife spawn. Look for model to spawn. + // 1. Animation projectile. + // 2. Weapon model projectile. + // 3. Base model projectile. + // 4. Legacy default. + if(projectile->star >= 0) + { + index = projectile->star; + + projectile_prime |= PROJECTILE_PRIME_SOURCE_PROJ_STAR; + } + else if (parent->weapent && parent->weapent->modeldata.subtype == SUBTYPE_PROJECTILE && parent->weapent->modeldata.project >= 0) + { + index = parent->weapent->modeldata.project; + + projectile_prime |= PROJECTILE_PRIME_SOURCE_WEAPON_PROJECTILE; + } + else if(parent->modeldata.star >= 0) + { + index = parent->modeldata.star; + + projectile_prime |= PROJECTILE_PRIME_SOURCE_MODEL_STAR; + } + else + { + index = get_cached_model_index("Star"); + + projectile_prime |= PROJECTILE_PRIME_SOURCE_GLOBAL_STAR; + } + + projectile_prime |= PROJECTILE_PRIME_BASE_Y; + projectile_prime |= PROJECTILE_PRIME_LAUNCH_MOVING; + + // Loop to max star count. + for(i = 0; i < MAX_STARS; i++) + { + // Spawn the star entity. If we fail, exit and return false. + ent = spawn(position.x, position.z, position.y, direction, NULL, index, NULL); + if(ent == NULL) + { + return 0; + } + + // Get result of direction adjustment. We need this before we can handle + // positioning on X axis. + direction = direction_get_adjustment_result(ent, parent, projectile->direction_adjust); + + /* + * Apply default X position if + * creator did not give us a value. + */ + + if (projectile->position.x == PROJECTILE_LEGACY_COMPATABILITY_POSITION_X) + { + projectile->position.x = PROJECTILE_DEFAULT_STAR_POSITION_X; + } + + // Reverse X when parent faces left. + if (direction == DIRECTION_RIGHT) + { + position.x = parent->position.x + projectile->position.x; + } + else + { + position.x = parent->position.x - projectile->position.x; + } + + ent->position.x = position.x; + ent->direction = direction; + + // 2019-12-17 DC - Not sure why we set attacking off, but + // leaving it here for legacy behavior. + parent->attacking = ATTACKING_NONE; + + // First star spawned sort id serves as a base for sorting. + // Then the next star's sort is the base - loop index. Each + // subsequent star appears one step further behind in + // sorting order. + // + // Ex: Base (first star) = 20, 20 - 1 = 19, 20 - 2 = 18. + if (i <= 0) + { + first_sortid = ent->sortid; + } + + ent->sortid = first_sortid - i; + + ent->takedamage = arrow_takedamage; + ent->owner = parent; + ent->attacking = ATTACKING_ACTIVE; + ent->nograb = 1; + + // Get the star velocity setting from animation. + ent->velocity.x = projectile->star_velocity[i]; + + // Reverse X velocity if direction is left. + if (direction == DIRECTION_LEFT) + { + ent->velocity.x = -ent->velocity.x; + } + + ent->think = common_think; + ent->nextthink = _time + 1; + ent->trymove = NULL; + ent->takeaction = NULL; + ent->modeldata.aimove = AIMOVE1_STAR; + ent->modeldata.aiattack = AIATTACK1_NOATTACK; + + // Remove star on contact. + if (ent->modeldata.remove_config & REMOVE_CONFIG_HIT) + { + ent->autokill |= AUTOKILL_ATTACK_HIT; + } + + ent->position.y = position.y; + ent->base = position.y; + ent->speedmul = 2; + + /* + * Copy faction data. + */ + + faction_copy_all(ent, parent); + + // Basic terrian property setup. + ent->modeldata.move_config_flags |= (MOVE_CONFIG_SUBJECT_TO_BASEMAP | MOVE_CONFIG_SUBJECT_TO_GRAVITY | MOVE_CONFIG_SUBJECT_TO_HOLE | MOVE_CONFIG_SUBJECT_TO_PLATFORM | MOVE_CONFIG_SUBJECT_TO_WALL); + ent->modeldata.move_config_flags &= ~MOVE_CONFIG_NO_ADJUST_BASE; + + ent->spawntype = SPAWN_TYPE_PROJECTILE_STAR; + ent->projectile_prime = projectile_prime; + + // Execute the projectile's on spawn event. + execute_onspawn_script(ent); + } + return TRUE; +} + + + +void steam_think() +{ + if(!self->animating) + { + kill_entity(self, KILL_ENTITY_TRIGGER_STEAM_ANIMATION_COMPLETE); + return; + } + + self->base += 1; + self->position.y = self->base; +} + + + +// for the "trap" type 7-1-2005 trap start +void trap_think() +{ + self->attacking = ATTACKING_ACTIVE; +} +// 7-1-2005 trap end + + + + +void steam_spawn(float x, float z, float a) +{ + entity *e = NULL; + + e = spawn(x, z, a, 0, "Steam", -1, NULL); + + if(e == NULL) + { + return; + } + + e->spawntype = SPAWN_TYPE_STEAM; + e->base = a; + e->modeldata.move_config_flags |= MOVE_CONFIG_NO_ADJUST_BASE; + e->think = steam_think; + + // Execute the steams's on spawn event. + execute_onspawn_script(e); +} + + + +void steamer_think() +{ + steam_spawn(self->position.x, self->position.z, self->position.y); + self->nextthink = _time + (global_config.game_speed / 10) + (rand32() & 31); +} + + + +void text_think() // New function so text can be displayed +{ + // wait to suicide + if(!self->animating) + { + kill_entity(self, KILL_ENTITY_TRIGGER_TEXT_ANIMATION_COMPLETE); + } +} + +//////////////////////////////// + +//homing arrow find its target +// type : target type +entity *homing_find_target(entity* acting_entity) +{ + int i; + int min; + int max; + + entity* target_entity = NULL; + entity* cursor_entity; + + //use the walk animation's range + if(validanim(acting_entity, ANI_WALK)) + { + min = acting_entity->modeldata.animation[ANI_WALK]->range.x.min; + max = acting_entity->modeldata.animation[ANI_WALK]->range.x.max; + } + else + { + min = 0; + max = 999; + } + + //find the 'nearest' one + for(i = 0; i < ent_max; i++) + { + cursor_entity = ent_list[i]; + + if(cursor_entity->exists && cursor_entity != acting_entity // cant target self + && cursor_entity != acting_entity->owner // Don't go after owner. + && faction_check_is_hostile(acting_entity, cursor_entity) + && diff(cursor_entity->position.x, acting_entity->position.x) + diff(cursor_entity->position.z, acting_entity->position.z) >= min + && diff(cursor_entity->position.x, acting_entity->position.x) + diff(cursor_entity->position.z, acting_entity->position.z) <= max + && cursor_entity->animation->vulnerable[cursor_entity->animpos] ) + { + if(!target_entity || diff(cursor_entity->position.x, acting_entity->position.x) + diff(cursor_entity->position.z, acting_entity->position.z) < diff(target_entity->position.x, acting_entity->position.x) + diff(target_entity->position.z, acting_entity->position.z)) + { + target_entity = cursor_entity; + } + } + } + + return target_entity; +} + + +void bike_crash() +{ + int i; + if(self->direction == DIRECTION_RIGHT) + { + self->velocity.x = 2; + } + else + { + self->velocity.x = -2; + } + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if (savedata.joyrumble[i]) control_rumble(i, 1, 100); + } + //if(self->position.x < advancex-100 || self->position.x > advancex+(videomodes.hRes+100)) kill_entity(self); +} + + + +int biker_takedamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, int fall_flag, const s_defense* defense_object) +{ + entity *driver = NULL; + + if(target_entity->death_state & DEATH_STATE_DEAD) + { + return 0; + } + // Fell in a hole + if(target_entity->position.y < PIT_DEPTH) + { + kill_entity(target_entity, KILL_ENTITY_TRIGGER_TAKE_DAMAGE_BIKER_PIT); + return 0; + } + if(attacking_entity != target_entity) + { + set_opponent(attacking_entity, target_entity); + } + + if(attack_object->no_pain) // don't drop driver until it is dead, because the attack has no pain effect + { + checkdamage(target_entity, attacking_entity, attack_object, defense_object); + if(target_entity->energy_state.health_current > 0) + { + return 1; // not dead yet + } + } + + check_backpain(attacking_entity, target_entity); + set_pain(target_entity, target_entity->last_damage_type, 1); + target_entity->attacking = ATTACKING_ACTIVE; + if(!target_entity->modeldata.offscreenkill) + { + target_entity->modeldata.offscreenkill = 100; + } + target_entity->think = bike_crash; + // well, this is the real entity, the driver who take the damage + if((driver = drop_driver(target_entity))) + { + driver->position.y = target_entity->position.y; + driver->drop = 1; + driver->direction = target_entity->direction; + if(driver->takedamage) + { + driver->takedamage(driver, attacking_entity, attack_object, fall_flag, defense_object); + } + else + { + driver->energy_state.health_current -= attack_object->attack_force; + } + } + target_entity->energy_state.health_current = 0; + checkdeath(target_entity); + return 1; +} + + + +void obstacle_fall() +{ + if(inair(self)) + { + return; + } + + self->velocity.x = self->velocity.z = 0; + if((!self->animating && validanim(self, ANI_DIE)) || !validanim(self, ANI_DIE)) + { + kill_entity(self, KILL_ENTITY_TRIGGER_OBSTACLE_FALL_NO_DEATH_ANIMATION); // Fixed so ANI_DIE can be used + } +} + + + +void obstacle_fly() // Now obstacles can fly when hit like on Simpsons/TMNT +{ + //self->position.x += self->velocity.x * 4; // Equivelant of speed 40 + if(self->position.x > advancex + (videomodes.hRes + 200) || self->position.x < advancex - 200) + { + kill_entity(self, KILL_ENTITY_TRIGGER_OBSTACLE_FLY_OUT_OF_BOUNDS); + } +} + + + +int obstacle_takedamage(entity* target_entity, entity* attacking_entity, s_attack* attack_object, int fall_flag, const s_defense* defense_object) +{ + if(target_entity->position.y <= PIT_DEPTH) + { + kill_entity(target_entity, KILL_ENTITY_TRIGGER_TAKE_DAMAGE_OBSTACLE_PIT); + return 0; + } + + set_opponent(attacking_entity, target_entity); + if(target_entity->opponent && (target_entity->opponent->modeldata.type & TYPE_PLAYER)) + { + if (savedata.joyrumble[target_entity->opponent->playerindex]) control_rumble(target_entity->opponent->playerindex, 1, 75); + } + + /* Calculate and apply HP damage. */ + checkdamage(target_entity, attacking_entity, attack_object, defense_object); + + target_entity->playerindex = attacking_entity->playerindex; // Added so points go to the correct player + addscore(attacking_entity->playerindex, attack_object->attack_force * target_entity->modeldata.multiple); // Points can now be given for hitting an obstacle + + if(target_entity->energy_state.health_current <= 0) + { + + checkdeath(target_entity); + + if(attacking_entity->position.x < target_entity->position.x) + { + target_entity->velocity.x = 1; + } + else + { + target_entity->velocity.x = -1; + } + + target_entity->attacking = ATTACKING_ACTIVE; // So obstacles can explode and hurt players/enemies + + if(target_entity->modeldata.subtype == SUBTYPE_FLYDIE) // Now obstacles can fly like on Simpsons/TMNT + { + target_entity->velocity.x *= 4; + target_entity->think = obstacle_fly; + ent_set_anim(target_entity, ANI_FALL, 0); + } + else + { + target_entity->think = obstacle_fall; + + if(validanim(target_entity, ANI_DIE)) + { + ent_set_anim(target_entity, ANI_DIE, 0); // LTB 1-13-05 Die before toss + } + else + { + toss(target_entity, target_entity->modeldata.jumpheight / 1.333); + ent_set_anim(target_entity, ANI_FALL, 0); + } + + if(target_entity->modeldata.death_config_flags & DEATH_CONFIG_MACRO_BLINK) + { + target_entity->blink = 1; + } + } + } + + target_entity->nextthink = _time + 1; + return 1; +} + +// Caskey, Damon V. +// 2018-04-27 +// +// Allocate memory and set entity properties that will be transfered +// to a dropped item. +void initialize_item_carry(entity *ent, s_spawn_entry *spawn_entry) +{ + // It's possible to call this from script, so if + // there is already memory for an item allocated + // here, clear it out to make sure we don't end up + // with any memory leaks. + if(ent->item_properties) + { + free(ent->item_properties); + ent->item_properties = NULL; + } + + // Allocate memory for the item. + ent->item_properties = malloc(sizeof(*ent->item_properties)); + memset(ent->item_properties, 0, sizeof(*ent->item_properties)); + + if(spawn_entry) + { + ent->item_properties->index = spawn_entry->item_properties.index; + + if(spawn_entry->item_properties.alias[0]) + { + strcpy(ent->item_properties->alias, spawn_entry->item_properties.alias); + } + + if(spawn_entry->item_properties.colorset) + { + ent->item_properties->colorset = spawn_entry->item_properties.colorset; + } + + if(spawn_entry->item_properties.health) + { + ent->item_properties->health = spawn_entry->item_properties.health; + } + ent->item_properties->player_count = spawn_entry->item_properties.player_count; + } +} + +entity *smartspawn(s_spawn_entry *props) // 7-1-2005 Entire section replaced with lord balls code +{ + entity *e = NULL; + entity *wp = NULL; + int playercount; + + if( props == NULL /* || + (level == NULL && + !(screen_status & (IN_SCREEN_SELECT | IN_SCREEN_TITLE | IN_SCREEN_HALL_OF_FAME | IN_SCREEN_GAME_OVER | IN_SCREEN_SHOW_COMPLETE | IN_SCREEN_ENGINE_CREDIT | IN_SCREEN_MENU | IN_SCREEN_GAME_START_MENU | IN_SCREEN_NEW_GAME_MENU | IN_SCREEN_LOAD_GAME_MENU | IN_SCREEN_OPTIONS_MENU | IN_SCREEN_CONTROL_OPTIONS_MENU | IN_SCREEN_SOUND_OPTIONS_MENU | IN_SCREEN_VIDEO_OPTIONS_MENU | IN_SCREEN_SYSTEM_OPTIONS_MENU)) && + currentScene)*/ + ) + { + return NULL; + } + + /* Spawn item based on number of active players. */ + if(props->spawnplayer_count >= (playercount = MAX(1, count_ents(TYPE_PLAYER)))) + { + if(props->boss && level != NULL) + { + --level->bossescount; + } + return NULL; + } + + if( level != NULL && ((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD)) ) + { + e = spawn(props->position.x, props->position.z + advancey, props->position.y, props->flip, props->name, props->index, props->model); + } + else + { + e = spawn(props->position.x + advancex, props->position.z, props->position.y, props->flip, props->name, props->index, props->model); + } + + if(e == NULL) + { + return NULL; + } + + //printf("%s, (%f, %f, %f) - (%f, %f, %f)", props->name, props->position.x, props->position.z, props->position.y, e->position.x, e->position.z, e->position.y); + + // Alias? + if(props->alias[0]) + { + memset(e->name, 0, sizeof(e->name)); + strcpy(e->name, props->alias); + } + + // If we have item properties in spawn entry, then prepare a set of + // properties to pass on to the item when it is dropped. + if(props->item) + { + initialize_item_carry(e, props); + } + + faction_copy_data(&e->modeldata.faction, &props->faction); + faction_copy_data(&e->faction, &props->faction); + + if(props->spawntype) + { + e->spawntype = props->spawntype; //2011_03_23, Pass spawntype. + } + + if(props->health[playercount - 1] != 0) + { + e->energy_state.health_current = e->modeldata.health = props->health[playercount - 1]; + } + + if(props->mp != 0) + { + e->energy_state.mp_current = e->modeldata.mp = props->mp; + } + + if(props->score != 0) + { + e->modeldata.score = props->score; // Overwrite score if exists in the level's. txt file + } + if(props->multiple != 0) + { + e->modeldata.multiple = props->multiple; // Overwrite multiple if exists in the level's .txt file + } + + if(!e->map && props->colourmap) + { + ent_set_colourmap(e, props->colourmap); + } + + if(props->aggression) + { + e->modeldata.aggression = props->aggression; // Aggression can be changed with spawn points now + } + if(props->item_properties.alpha) + { + e->item_properties->alpha = props->item_properties.alpha; + } + if(props->alpha) + { + e->modeldata.alpha = props->alpha; + } + + // Feb 26, 2005 - Store the original map to be able to restore with dying flash + if(props->dying) + { + e->dying = props->dying; // Feb 26, 2005 - Used to define which colourmap is used for the dying flash + e->per1 = props->per1; // Mar 21, 2005 - Used to store custom percentages + e->per2 = props->per2; // Mar 21, 2005 - Used to store custom percentages + e->dying2 = props->dying2; // Dec 15, 2016 - Used to define which colourmap is used for the dying flash for per2 By White Dragon + } + + if(props->nolife) + { + e->modeldata.nolife = props->nolife; // Overwrite whether live is visible or not + } + e->boss = props->boss; + + if(props->boss && level != NULL && level->bossmusic[0]) + { + music(level->bossmusic, 1, level->bossmusic_offset); + } + + // give the entity a weapon item + if(props->weapon) + { + wp = spawn(e->position.x, ITEM_HIDE_POSITION_Z, 0, 0, props->weapon, props->weaponindex, props->weaponmodel); + if(wp) + { + //ent_default_init(wp); + set_weapon(e, wp->modeldata.weapon_properties.weapon_index, 0); + e->weapent = wp; + + e->weapent->spawntype = SPAWN_TYPE_WEAPON; + } + } + + // set entity type: player, enemy, npc... + if(props->entitytype) + { + e->modeldata.type = props->entitytype; + } + + // set a parent + if(props->parent) //->varlist->vars->vt != VT_EMPTY + { + e->parent = (entity *)props->parent; + } + + //ent_default_init(e); + execute_onspawn_script(e); + execute_spawn_script(props, e); + return e; +} // 7-1-2005 replaced section ends here + +int is_incam(float x, float z, float a, float threshold) +{ + if (level) + { + if ( x >= advancex+threshold && x <= advancex+videomodes.hRes-threshold && z-a >= advancey+threshold && z-a <= advancey+videomodes.vRes-4 ) { + if ( x >= scrollminx && x <= scrollmaxx+videomodes.hRes && z >= PLAYER_MIN_Z && z <= PLAYER_MAX_Z ) { + return 1; + } + } + } + + return 0; +} + +void spawnplayer(int index) +{ + s_spawn_entry p; + //s_model * model = NULL; + int wall; + int xc, zc, find = 0; + index &= 3; + +// model = find_model(player[index].name); +// if(model == NULL) return; + + memset(&p, 0, sizeof(p)); + p.name = player[index].name; + p.index = -1; + p.item_properties.index = -1; + p.weaponindex = -1; + p.colourmap = player[index].colourmap; + p.spawnplayer_count = -1; + p.spawntype = SPAWN_TYPE_PLAYER_MAIN; + + if(level->scrolldir & SCROLL_LEFT) + { + if(level->spawn && level->spawn[index].x) + { + p.position.x = (float)(videomodes.hRes - level->spawn[index].x); + } + else + { + p.position.x = (float)((videomodes.hRes - 20) - 30 * index); + } + } + else + { + if(level->spawn && level->spawn[index].x) + { + p.position.x = (float)(level->spawn[index].x); + } + else + { + p.position.x = (float)(20 + 30 * index); + } + p.flip = 1; + } + + if(level->spawn && level->spawn[index].z) + { + if(level->scrolldir & (SCROLL_INWARD | SCROLL_OUTWARD)) + { + p.position.z = (float)(level->spawn[index].z); + } + else + { + p.position.z = (float)(PLAYER_MIN_Z + level->spawn[index].z); + } + } + else if(PLAYER_MAX_Z - PLAYER_MIN_Z > 5) + { + p.position.z = (float)(PLAYER_MIN_Z + 5); + } + else + { + p.position.z = (float)PLAYER_MIN_Z; + } + + if(p.position.z < PLAYER_MIN_Z) + { + p.position.z = PLAYER_MIN_Z; + } + else if(p.position.z > PLAYER_MAX_Z) + { + p.position.z = PLAYER_MAX_Z; + } + + //////////////////checking holes/ walls/////////////////////////////////// + for(xc = 0; xc < videomodes.hRes / 4; xc++) + { + if(p.position.x >= videomodes.hRes) + { + p.position.x -= videomodes.hRes; + } + if(p.position.x < 0) + { + p.position.x += videomodes.hRes; + } + if(PLAYER_MIN_Z == PLAYER_MAX_Z) + { + wall = checkwall_index(advancex + p.position.x, p.position.z); + if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT) + { + break; //found + } + if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y) || (wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT)) + { + find = 0; + } + else + { + break; // found + } + } + else for(zc = 0; zc < (PLAYER_MAX_Z - PLAYER_MIN_Z) / 3; zc++, p.position.z += 3) + { + if(p.position.z > PLAYER_MAX_Z) + { + p.position.z -= PLAYER_MAX_Z - PLAYER_MIN_Z; + } + if(p.position.z < PLAYER_MIN_Z) + { + p.position.z += PLAYER_MAX_Z - PLAYER_MIN_Z; + } + wall = checkwall_index(advancex + p.position.x, p.position.z); + if(wall >= 0 && level->walls[wall].height < MAX_WALL_HEIGHT) + { + find = 1; + break; + } + else if(wall >= 0 && level->walls[wall].height >= MAX_WALL_HEIGHT) + { + continue; + } + if(checkhole_in(advancex + p.position.x, p.position.z, p.position.y)) + { + continue; + } + find = 1; + break; + } + if(find) + { + break; + } + p.position.x += (level->scrolldir & SCROLL_LEFT) ? -4 : 4; + } + /////////////////////////////////////////////////////////////////////// + currentspawnplayer = index; + player[index].ent = smartspawn(&p); + + if(player[index].ent == NULL) + { + borShutdown(1, "Fatal: unable to spawn player from '%s'\n", p.name); + } + + player[index].ent->playerindex = index; + + if(nomaxrushreset[4] >= 1) + { + player[index].ent->rush.max = nomaxrushreset[index]; + } + else + { + player[index].ent->rush.max = 0; + } + + memset( + player[index].command_input_history, + 0, + sizeof(player[index].command_input_history) + ); + + memset( + player[index].command_input_hold_start_time, + 0, + sizeof(player[index].command_input_hold_start_time) + ); + + memset( + player[index].command_input_hold_trigger_time, + 0, + sizeof(player[index].command_input_hold_trigger_time) + ); + + player[index].command_input_hold_start_valid = 0; + player[index].command_input_hold_trigger_valid = 0; + player[index].command_input_count = 0; + player[index].command_input_index = 0; + + if(player[index].spawnhealth) + { + player[index].ent->energy_state.health_current = player[index].spawnhealth + 5; + } + if(player[index].ent->energy_state.health_current > player[index].ent->modeldata.health) + { + player[index].ent->energy_state.health_current = player[index].ent->modeldata.health; + } + + //mp little recorver after a level by tails + if(player[index].spawnmp) + { + player[index].ent->energy_state.mp_current = player[index].spawnmp + 2; + } + if(player[index].ent->energy_state.mp_current > player[index].ent->modeldata.mp) + { + player[index].ent->energy_state.mp_current = player[index].ent->modeldata.mp; + } + + if(player[index].weapnum) + { + set_weapon(player[index].ent, player[index].weapnum, 0); + } + else + { + set_weapon(player[index].ent, level->setweap, 0); + } +} + +int no_player_alive_to_join() +{ + int no_alive_players = 0; + int i; + for(i = 0; i < MAX_PLAYERS; i++) + { + if( ((!player[i].ent || player[i].lives <= 0 || (player[i].lives <= 1 && player[i].ent->energy_state.health_current <= 0)) && + ((noshare && player[i].credits <= 0) || (!noshare && credits <= 0))) + ) + { + ++no_alive_players; + } + } + no_alive_players = (no_alive_players >= MAX_PLAYERS) ? 1 : 0; + + return no_alive_players; +} + +void kill_all_players_by_timeover() +{ + int i; + s_attack attack_timeover = emptyattack; + s_attack attack_lose = emptyattack; + const s_defense* defense_object = NULL; + + attack_timeover.attack_type = ATK_TIMEOVER; + attack_timeover.dropv.y = default_model_dropv.y; + attack_timeover.dropv.x = default_model_dropv.x; + attack_timeover.dropv.z = default_model_dropv.z; + + attack_lose.attack_type = ATK_LOSE; + + + endgame = 1; + for(i = 0; i < MAX_PLAYERS; i++) + { + entity* tmp = self; + self = player[i].ent; + if(self && !validanim(self, ANI_LOSE)) + { + endgame = 0; + attack_timeover.attack_force = self->energy_state.health_current; + + defense_object = defense_find_current_object(self, NULL, attack_timeover.attack_type); + + self->takedamage(self, self, &attack_timeover, 0, defense_object); + } + else if(self) + { + endgame = 0; + + attack_lose.attack_force = self->energy_state.health_current; + if (inair(self) && validanim(self, ANI_FALLLOSE)) + { + attack_lose.dropv.y = default_model_dropv.y; + attack_lose.dropv.x = default_model_dropv.x; + attack_lose.dropv.z = default_model_dropv.z; + self->modeldata.death_config_flags |= DEATH_CONFIG_MACRO_DEATH_FALL_LAND; + } + else + { + self->modeldata.death_config_flags |= DEATH_CONFIG_MACRO_DEATH; + } + + defense_object = defense_find_current_object(self, NULL, attack_lose.attack_type); + + self->takedamage(self, self, &attack_lose, 0, defense_object); + } + self = tmp; + } +} + +void time_over() +{ + if(level->type == 1) + { + level_completed = 1; // Feb 25, 2005 - Used for bonus levels so a life isn't taken away if time expires.level->type == 1 means bonus level, else regular + } + else if(!level_completed) + { + kill_all_players_by_timeover(); + + if (!is_total_timeover) + { + if(global_sample_list.time_over >= 0) + { + sound_play_sample(global_sample_list.time_over, 0, savedata.effectvol, savedata.effectvol, 100); + } + + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + if(!endgame) + { + showtimeover = 1; + } + } + if (!is_total_timeover && no_player_alive_to_join()) + { + is_total_timeover = 1; + nojoin = 1; + } + } +} + + +// ----------------------- Update functions ------------------------------ + +void update_scroller() +{ + float to = 0; + int i, againstend = 0; + int numplay = 0; //4player + float tx = advancex, ty = advancey; + float rm = -9999, lm = 999999, bm = -9999, tm = 999999; //player boundary box + static int scrolladd = 0; + scrolldx = scrolldy = 0; + int p_alive = 0; + + if(_time < level->advancetime || freezeall) + { + return; // Added freezeall so backgrounds/scrolling don't update if animations are frozen + } + + /* + //level->advancetime = _time + (global_config.game_speed/100); // Changed so scrolling speeds up for faster players + level->advancetime = _time - + ((player[0].ent && (player[0].ent->modeldata.speed.x >= 12 || player[0].ent->modeldata.runspeed >= 12)) || + (player[1].ent && (player[1].ent->modeldata.speed.x >= 12 || player[1].ent->modeldata.runspeed >= 12)) || + (player[2].ent && (player[2].ent->modeldata.speed.x >= 12 || player[2].ent->modeldata.runspeed >= 12)) || + (player[3].ent && (player[3].ent->modeldata.speed.x >= 12 || player[3].ent->modeldata.runspeed >= 12)) ); // Changed so if your player is faster the backgrounds scroll faster*/ + + level->advancetime = _time; + + if(level_completed) + { + return; + } + + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].ent && !(player[i].ent->death_state & DEATH_STATE_DEAD)) + { + p_alive = 1; + break; + } + } + + //White Dragon: No more enemies! + if(current_spawn >= level->numspawns && !findent(TYPE_ENEMY) && p_alive) + { + if(!findent(TYPE_ENDLEVEL) && ((!findent(TYPE_ITEM | TYPE_OBSTACLE) && level->type == 1) || level->type == 0)) // Feb 25, 2005 - Added so obstacles + { + level_completed = 1; // can be used for bonus levels + } + } + else if(count_ents(TYPE_ENEMY) < groupmin) + { + while(count_ents(TYPE_ENEMY) < groupmax && + current_spawn < level->numspawns && + level->pos >= level->spawnpoints[current_spawn].at + ) + { + if(level->spawnpoints[current_spawn].musicfade) + { + musicfade[0] = (float)level->spawnpoints[current_spawn].musicfade; + musicfade[1] = (float)savedata.musicvol; + } + else if(level->spawnpoints[current_spawn].music[0]) + { + strncpy(musicname, level->spawnpoints[current_spawn].music, MAX_BUFFER_LEN); + musicoffset = level->spawnpoints[current_spawn].musicoffset; + musicloop = 1; + } + else if(level->spawnpoints[current_spawn].wait) + { + level->waiting = 1; + go_time = 0; + } + else if(level->spawnpoints[current_spawn].groupmin || level->spawnpoints[current_spawn].groupmax) + { + groupmin = level->spawnpoints[current_spawn].groupmin; + groupmax = level->spawnpoints[current_spawn].groupmax; + } + else if(level->spawnpoints[current_spawn].nojoin != 0) + { + nojoin = (level->spawnpoints[current_spawn].nojoin == 1); + } + else if(level->spawnpoints[current_spawn].scrollminz & 0x80000000) + { + scrollminz = (float)(level->spawnpoints[current_spawn].scrollminz & 0x7fffffff); + scrollmaxz = (float)level->spawnpoints[current_spawn].scrollmaxz; + if(!_time) + { + advancey = scrollminz; // reset y if spawn at very beginning + } + } + else if(level->spawnpoints[current_spawn].scrollminx & 0x80000000) + { + scrollminx = (float)(level->spawnpoints[current_spawn].scrollminx & 0x7fffffff); + scrollmaxx = (float)level->spawnpoints[current_spawn].scrollmaxx; + } + else if(level->spawnpoints[current_spawn].blockade) + { + // assume level spawn entry will not roll back, so just change it to 0 here + if(level->spawnpoints[current_spawn].blockade < 0) + { + level->spawnpoints[current_spawn].blockade = 0; + } + blockade = (float)level->spawnpoints[current_spawn].blockade; + } + else if(level->spawnpoints[current_spawn].palette != 0) + { + // assume level spawn entry will not roll back, so just change it to 0 here + if(level->spawnpoints[current_spawn].palette < 0) + { + level->spawnpoints[current_spawn].palette = 0; + } + change_system_palette(level->spawnpoints[current_spawn].palette); + } + else if(level->spawnpoints[current_spawn].light.y) // change light direction for gfxshadow + { + light.x = level->spawnpoints[current_spawn].light.x; + light.y = level->spawnpoints[current_spawn].light.y; + } + else if(level->spawnpoints[current_spawn].shadowcolor) // change color for gfxshadow + { + shadowcolor = level->spawnpoints[current_spawn].shadowcolor; + if(shadowcolor == -1) + { + shadowcolor = 0; + } + else if(shadowcolor == -2) + { + shadowcolor = -1; + } + } + else if(level->spawnpoints[current_spawn].shadowalpha) // change color for gfxshadow + { + shadowalpha = level->spawnpoints[current_spawn].shadowalpha; + if(shadowalpha == BLEND_MODE_MODEL) + { + shadowalpha = BLEND_MODE_NONE; + } + } + else if(level->spawnpoints[current_spawn].shadowopacity) // change color for gfxshadow + { + shadowopacity = level->spawnpoints[current_spawn].shadowopacity; + if(shadowopacity == -1) + { + shadowopacity = 0; + } + if(shadowopacity == -2) + { + shadowopacity = -1; + } + } + else + { + smartspawn(&level->spawnpoints[current_spawn]); + } + ++current_spawn; + } + } + + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].ent) + { + if(player[i].ent->position.x > rm) + { + rm = player[i].ent->position.x; + } + if(player[i].ent->position.x < lm) + { + lm = player[i].ent->position.x; + } + if(player[i].ent->position.z > bm) + { + bm = player[i].ent->position.z; + } + if(player[i].ent->position.z < tm) + { + tm = player[i].ent->position.z; + } + numplay++; + } + } + + if(level->waiting) + { + // Wait for all enemies to be defeated + if(!findent(TYPE_ENEMY)) + { + level->waiting = 0; + if(level->noreset <= 1) + { + timeleft = level->settime * global_config.counter_speed; // Feb 24, 2005 - This line moved here to set custom time + } + go_time = _time + 3 * global_config.game_speed; + } + } + + if(numplay == 0) + { + return; + } + + + + if(!level->waiting || global_config.cheats & CHEAT_OPTIONS_IMPLACABLE_ACTIVE) + { + if(level->scrolldir & SCROLL_RIGHT) + { + + againstend = (level->width <= videomodes.hRes); + + if(rm - lm > videomodes.hRes) + { + to = advancex; + } + else + { + to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset; + } + + if(to < scrollminx) + { + to = scrollminx; + } + else if(to > scrollmaxx) + { + to = scrollmaxx; + } + + if((level->scrolldir & SCROLL_BACK) && to < blockade) + { + to = blockade; + } + + if(to > advancex) + { + if(to > advancex + level->scrollspeed) + { + to = advancex + level->scrollspeed; + } + advancex = to; + } + else if((level->scrolldir & SCROLL_BACK) && to < advancex) + { + if(to < advancex - level->scrollspeed) + { + to = advancex - level->scrollspeed; + } + advancex = to; + } + + if(advancex < 0) + { + advancex = 0; + } + if(advancex >= level->width - videomodes.hRes) + { + advancex = (float)level->width - videomodes.hRes; + againstend = 1; + } + + if(againstend) + { + level->pos++; + } + else + { + level->pos = (int)advancex; + } + + + } + else if(level->scrolldir & SCROLL_LEFT) + { + + againstend = (level->width <= videomodes.hRes); + + if(rm - lm > videomodes.hRes) + { + to = advancex; + } + else + { + to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset; + } + + if(to < scrollminx) + { + to = scrollminx; + } + else if(to > scrollmaxx) + { + to = scrollmaxx; + } + + if((level->scrolldir & SCROLL_BACK) && level->width - videomodes.hRes - to < blockade) + { + to = level->width - videomodes.hRes - blockade; + } + + if(to < advancex) + { + if(to < advancex - level->scrollspeed) + { + to = advancex - level->scrollspeed; + } + advancex = to; + } + else if((level->scrolldir & SCROLL_BACK) && to > advancex) + { + if(to > advancex + level->scrollspeed) + { + to = advancex + level->scrollspeed; + } + advancex = to; + } + + if(advancex > level->width - videomodes.hRes) + { + advancex = (float)level->width - videomodes.hRes; + } + if(advancex <= 0) + { + advancex = 0; + againstend = 1; + } + + if(againstend) + { + level->pos++; + } + else + { + level->pos = (int)((level->width - videomodes.hRes) - advancex); + } + } + else if(level->scrolldir & SCROLL_OUTWARD) // z scroll only + { + + if(bm - tm > videomodes.vRes) + { + to = advancey; + } + else + { + to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset; + } + + if(to < scrollminz) + { + to = scrollminz; + } + else if(to > scrollmaxz) + { + to = scrollmaxz; + } + + if((level->scrolldir & SCROLL_BACK) && to < blockade) + { + to = blockade; + } + + if(to > advancey) + { + if(to > advancey + level->scrollspeed) + { + to = advancey + level->scrollspeed; + } + advancey = to; + } + else if((level->scrolldir & SCROLL_BACK) && to < advancey) + { + if(to < advancey - level->scrollspeed) + { + to = advancey - level->scrollspeed; + } + advancey = to; + } + + if(advancey > panel_height - videomodes.vRes) + { + advancey = (float)panel_height - videomodes.vRes; + againstend = 1; + } + if(advancey < 0) + { + advancey = 0; + } + + if(againstend) + { + level->pos++; + } + else + { + level->pos = (int)advancey; + } + } + else if(level->scrolldir & SCROLL_INWARD) + { + if(bm - tm > videomodes.vRes) + { + to = advancey; + } + else + { + to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset; + } + + if(to < scrollminz) + { + to = scrollminz; + } + else if(to > scrollmaxz) + { + to = scrollmaxz; + } + + if((level->scrolldir & SCROLL_BACK) && panel_height - videomodes.vRes - to < blockade) + { + to = panel_height - videomodes.vRes - blockade; + } + + if(to < advancey) + { + if(to < advancey - level->scrollspeed) + { + to = advancey - level->scrollspeed; + } + advancey = to; + } + else if((level->scrolldir & SCROLL_BACK) && to > advancey) + { + if(to > advancey + level->scrollspeed) + { + to = advancey + level->scrollspeed; + } + advancey = to; + } + + if(advancey > panel_height - videomodes.vRes) + { + advancey = (float)panel_height - videomodes.vRes; + } + if(advancey <= 0) + { + advancey = 0; + againstend = 1; + } + + if(againstend) + { + level->pos++; + } + else + { + level->pos = (int)((panel_height - videomodes.vRes) - advancey); + } + } + //up down, elevator stage + else if(level->scrolldir & (SCROLL_UP | SCROLL_DOWN)) + { + //advancey += 0.5; + if(scrolladd == 1) + { + scrolladd = 0; + advancey++; + } + else + { + scrolladd++; + } + level->pos = (int)advancey; + } + }//if(!level->waiting) + + // z auto-scroll + if((level->scrolldir & SCROLL_LEFT) || (level->scrolldir & SCROLL_RIGHT)) // added scroll type both; weird things can happen, but only if the modder is lazy in using blockades, lol + { + + if(cameratype == 1) + { + bm = -9999; + tm = 999999; //recalculate + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].ent) + { + if(player[i].ent->position.z - player[i].ent->position.y > bm) + { + bm = player[i].ent->position.z - player[i].ent->position.y; + } + if(player[i].ent->position.z - player[i].ent->position.y < tm) + { + tm = player[i].ent->position.z - player[i].ent->position.y; + } + } + } + } + if(bm - tm > videomodes.vRes) + { + to = advancey; + } + else + { + to = (bm + tm) / 2 - videomodes.vRes / 2 + level->camerazoffset; + } + + // new scroll limit + if(to > scrollmaxz) + { + to = scrollmaxz; + } + else if(to < scrollminz) + { + to = scrollminz; + } + + if(to != advancey) + { + if(to > advancey + level->scrollspeed) + { + to = advancey + level->scrollspeed; + } + else if(to < advancey - level->scrollspeed) + { + to = advancey - level->scrollspeed; + } + advancey = (float)to; + } + + if(advancey > panel_height - (level->rocking ? 16 : 12) - videomodes.vRes) + { + advancey = (float)(panel_height - (level->rocking ? 16 : 12) - videomodes.vRes); + } + if(advancey < 0) + { + advancey = 0; + } + } + // now x auto scroll + else if((level->scrolldir & SCROLL_INWARD) || (level->scrolldir & SCROLL_OUTWARD)) + { + if(rm - lm > videomodes.hRes) + { + to = advancex; + } + else + { + to = (lm + rm) / 2 - videomodes.hRes / 2 + level->cameraxoffset; + } + + // new scroll limit + if(to > scrollmaxx) + { + to = scrollmaxx; + } + else if(to < scrollminx) + { + to = scrollminx; + } + + if(to != advancex) + { + if(to > advancex + level->scrollspeed) + { + to = advancex + level->scrollspeed; + } + else if(to < advancex - level->scrollspeed) + { + to = advancex - level->scrollspeed; + } + advancex = (float)to; + } + + if(advancex > level->width - videomodes.hRes) + { + advancex = (float)(level->width - videomodes.hRes); + } + if(advancex < 0) + { + advancex = 0; + } + } + //end of z auto-scroll + // global value for type_panel + scrolldx = advancex - tx; + scrolldy = advancey - ty; +} + + +void update_scrolled_bg() +{ + float rocktravel; + unsigned char neonp[32];//3*8 + static int rockpos = 0; + static int rockoffssine[32] = + { + 2, 2, 3, 4, 5, 6, 7, 7, + 8, 8, 9, 9, 9, 9, 8, 8, + 7, 7, 6, 5, 4, 3, 2, 2, + 1, 1, 0, 0, 0, 0, 1, 1 + }; // normal rock + static int rockoffsshake[32] = + { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 4, 2, 0, 4, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 4, 2, 0, 4, 2, + }; // slow, constant jarring rock, like on a train + static int rockoffsrumble[32] = + { + 2, 2, 3, 3, 2, 2, 3, 3, + 2, 2, 3, 3, 2, 3, 2, 3, + 2, 2, 3, 3, 2, 2, 3, 3, + 2, 2, 3, 3, 2, 3, 2, 3, + }; // fast, constant rumbling, like in/on a van or trailer + int pb = pixelbytes[(int)PIXEL_32]; + + // Time to update neon and screen all flag false? + if(_time >= neon_time && !freezeall) + { + memcpy(neonp, neontable + 128 * pb, 8 * pb); + memcpy(neontable + 128 * pb, neonp + 2 * pb, 6 * pb); + memcpy(neontable + (128 + 6)*pb, neonp, 2 * pb); + + neon_time = _time + (global_config.game_speed / 3); + } + + if(!freezeall) + { + rocktravel = (level->rocking) ? ((_time - traveltime) / ((float)global_config.game_speed / 30)) : 0; // no like in real life, maybe + if(level->bgspeed < 0) + { + rocktravel = -rocktravel; + } + bgtravelled += (_time - traveltime) * level->bgspeed / 30 * 4 + rocktravel; + vbgtravelled += (_time - traveltime) * level->vbgspeed / 30 * 4; + } + else + { + texttime += _time - traveltime; + } + + timevar = _time - texttime; + + if(level->rocking) + { + rockpos = (timevar / (global_config.game_speed / 8)) & 31; + if(level->rocking == 1) + { + gfx_y_offset = level->quake - 4 - rockoffssine[rockpos]; + } + else if(level->rocking == 2) + { + gfx_y_offset = level->quake - 4 - rockoffsshake[rockpos]; + } + else if(level->rocking == 3) + { + gfx_y_offset = level->quake - 4 - rockoffsrumble[rockpos]; + } + } + else + { + if(level->quake >= 0) + { + gfx_y_offset = level->quake - 4; + } + else + { + gfx_y_offset = level->quake + 4; + } + } + + //if(level->scrolldir!=SCROLL_UP && level->scrolldir!=SCROLL_DOWN) gfx_y_offset -= advancey; + gfx_y_offset += gfx_y_offset_adj; //2011_04_03, DC: Apply modder adjustment. + + traveltime = _time; + + if(_time >= level->quaketime) + { + level->quake /= 2; + level->quaketime = _time + (global_config.game_speed / 25); + } +} + +void draw_scrolled_bg() +{ + int index = 0, x, z, i = 0, j, l, m; + s_layer *layer; + int width, height; + + int vpx, vpy, vpw, vph; + + if(viewportw > 0) + { + vpx = viewportx; + vpy = viewporty; + vpw = viewportw; + vph = viewporth; + } + else + { + vpx = vpy = 0; + vpw = videomodes.hRes; + vph = videomodes.vRes; + } + + //if(level) printf("%d %d %d %d\n", vpx, vpy, vpw, vph); + + s_drawmethod screenmethod = plainmethod; + + /*s_drawmethod *pscreenmethod = &screenmethod; + for(i = 0; i < level->numholes; i++) + { + spriteq_add_sprite((int)(level->holes[i].x - screenx + gfx_x_offset), (int)(level->holes[i].z - level->holes[i].depth - screeny + gfx_y_offset), HOLE_Z, holesprite, pscreenmethod, 0); + }*/ + + for(index = 0; index < level->numlayersref; index++) + { + layer = level->layersref + index; + + screenmethod = layer->drawmethod; + + //printf("layer %d, handle:%u, z:%d\n", index, layer->gfx.handle, layer->position.z); + + // Layer must be enabled and have at least one instace, or we don't draw it. + if(!screenmethod.xrepeat || !screenmethod.yrepeat || !layer->enabled) + { + continue; + } + + width = screenmethod.xspan = layer->size.x + layer->spacing.x; + height = screenmethod.yspan = layer->size.y + layer->spacing.z; + + x = (int)(layer->offset.x - (advancex + bgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.x) ) ; + + //printf("layerxratio %f %d %f\n ", layer->xratio, x, layer->bgspeedratio); + + if((level->scrolldir & SCROLL_UP)) + { + //z = (int)(layer->offset.z + advancey * (1.0 - layer->ratio.z) ) ; + z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ; + } + else + { + //z = (int)(layer->offset.z - advancey * (1.0 - layer->ratio.z) ) ; + z = (int)(layer->offset.z - (advancey + vbgtravelled * layer->bgspeedratio) * (1.0 - layer->ratio.z) ) ; + } + + if(layer->quake) + { + x += gfx_x_offset; + z += gfx_y_offset; + //printf("%d y %d %d\n", index, gfx_y_offset, z); + } + + x -= vpx; + z -= vpy; + + + if(x < 0) + { + i = (-x) / width; + x %= width; + } + else + { + i = 0; + } + + if(i > 0 && screenmethod.water.watermode != WATER_MODE_SHEAR && screenmethod.water.amplitude) + { + i--; + x -= width; + } + + if(z < 0) + { + j = (-z) / height; + z %= height; + } + else + { + j = 0; + } + + if(layer->neon) + { + screenmethod.table = neontable; + } + else + { + if(current_palette > 0) + { + screenmethod.table = level->palettes[current_palette - 1]; + } + else + { + screenmethod.table = NULL; + } + } + + screenmethod.water.wavetime = (int)(timevar * screenmethod.water.wavespeed); + screenmethod.xrepeat = screenmethod.yrepeat = 0; + for(m = z; j < layer->drawmethod.yrepeat && m < vph; m += height, j++, screenmethod.yrepeat++); + for(l = x; i < layer->drawmethod.xrepeat && l < vpw + (screenmethod.water.watermode == WATER_MODE_SHEAR ? 0 : screenmethod.water.amplitude * 2); l += width, i++, screenmethod.xrepeat++); + + if(layer->gfx.screen->magic == screen_magic) + { + spriteq_add_screen(x + vpx, z + vpy, layer->z, layer->gfx.screen, &screenmethod, index); + } + else if(layer->gfx.sprite->magic == sprite_magic) + { + spriteq_add_frame(x + vpx, z + vpy, layer->z, layer->gfx.sprite, &screenmethod, index); + } + + //printf("******%d\t%d\t%d\t%d\t%d*****\n", x+vpx, z+vpy, layer->.z, screenmethod.xrepeat, screenmethod.yrepeat); + } + + +} + +u32 getinterval() +{ + interval = timer_getinterval(global_config.game_speed); // so interval can be logged into movie + if(interval > global_config.game_speed) + { + interval = 1; + } + if(interval > global_config.game_speed / 4) + { + interval = global_config.game_speed / 4; + } + return interval; +} + +// Caskey, Damon V. +// 2019-04-22 +// +// Run input scripts. Similar to keys, but +// execute before processing any command functions. +void execute_input_scripts(int player_index) +{ + s_player *player_obj = NULL; + + player_obj = player + player_index; + + if (!player_obj) + { + return; + } + + if (player_obj->newkeys || (keyscriptrate && player->keys) || player->releasekeys) + { + // 2019-04-22 Don't exist yet + //if (level) + //{ + + //execute_level_key_script(player_index, player); + //execute_entity_key_script(player.ent); + //} + //execute_key_script(player_index, player); + + execute_input_script_all(player_index); + } +} + +/* +* Caskey, Damon V. +* 2026-07-17 +* +* Add direction-relative command flags to a physical +* input mask. +* +* Left and right remain in the result so hard-coded +* commands can continue to inspect the physical input. +*/ +static key_mask_t command_input_resolve_direction( + const key_mask_t input_flags, + const entity* acting_entity +) { + key_mask_t resolved_flags = input_flags; + + if(!acting_entity) { + return resolved_flags; + } + + if(input_flags & FLAG_MOVELEFT) { + resolved_flags |= acting_entity->direction + ? FLAG_BACKWARD + : FLAG_FORWARD; + + } else if(input_flags & FLAG_MOVERIGHT) { + resolved_flags |= acting_entity->direction + ? FLAG_FORWARD + : FLAG_BACKWARD; + } + + return resolved_flags; +} + +/* +* Caskey, Damon V. +* 2026-07-17 - Orginal by Utunnels, unknow date. +* +* Update the input state for each player. This includes +* physical keys, command input history, and automatic +* held edges. Refactored to support direction-relative +* command flags and extensible command system. +*/ +void inputrefresh(int playrecmode) { + + int p; + s_player *acting_player; + + control_update(playercontrolpointers, MAX_PLAYERS); + + bothkeys = 0; + bothnewkeys = 0; + + for(p = 0; p < MAX_PLAYERS; p++) + { + acting_player = player + p; + + if (playrecmode != A_REC_PLAY) { + + acting_player->releasekeys = (playercontrolpointers[p]->keyflags | acting_player->keys) - playercontrolpointers[p]->keyflags; + acting_player->releasekeys &= ~acting_player->disablekeys; + acting_player->keys = playercontrolpointers[p]->keyflags & ~acting_player->disablekeys; + acting_player->newkeys = playercontrolpointers[p]->newkeyflags & ~acting_player->disablekeys; + acting_player->playkeys |= acting_player->newkeys; + acting_player->playkeys &= acting_player->keys; + acting_player->playkeys &= ~acting_player->disablekeys; + + } else { + // in play mode: add pressed keys to rec keys + acting_player->releasekeys |= (playercontrolpointers[p]->keyflags | acting_player->prevkeys) - playercontrolpointers[p]->keyflags; + acting_player->releasekeys &= ~acting_player->disablekeys; + acting_player->keys |= playercontrolpointers[p]->keyflags & ~acting_player->disablekeys; + acting_player->newkeys |= playercontrolpointers[p]->newkeyflags & ~acting_player->disablekeys; + acting_player->playkeys |= acting_player->newkeys; + acting_player->playkeys &= acting_player->keys; + acting_player->playkeys &= ~acting_player->disablekeys; + } + + execute_input_scripts(p); + + /* + * Reset command sequence if the last command was + * too long ago. + */ + if(acting_player->ent && acting_player->ent->command_time < _time) { + memset( + acting_player->command_input_history, + 0, + sizeof(acting_player->command_input_history) + ); + + acting_player->command_input_count = 0; + acting_player->command_input_index = 0; + } + + /* + * Build one event every refresh so automatic held + * thresholds can create an edge without a physical + * button change. Store it only when at least one + * press, release, or automatic held edge exists. + */ + { + s_command_input_event input_event = {0}; + + input_event.press = command_input_resolve_direction( + acting_player->newkeys, + acting_player->ent + ); + + input_event.release = command_input_resolve_direction( + acting_player->releasekeys, + acting_player->ent + ); + + /* + * Released keys were held immediately before this + * edge. Include them in the event snapshot so a + * command such as a[50] + ~a can test both facts. + */ + input_event.held = command_input_resolve_direction( + acting_player->keys + | acting_player->releasekeys, + acting_player->ent + ); + + input_event.time = _time; + + command_input_hold_start_update( + acting_player, + input_event.press, + input_event.time + ); + + if(input_event.press) { + /* + * Capture inputs still held at this positive + * edge. The configurable command matcher + * applies each step's chord_time to their + * recorded press times. Hard-coded commands + * continue to inspect input_event.press only. + */ + input_event.press_chord = + command_input_resolve_direction( + acting_player->keys, + acting_player->ent + ); + } + + input_event.hold = + command_input_hold_trigger_collect( + acting_player, + input_event.held, + input_event.time + ); + + if(input_event.press + || input_event.hold + || input_event.release) { + + input_event.ticks = timer_gettick(); + command_input_history_push(acting_player, &input_event); + + if(acting_player->ent) { + acting_player->ent->command_time = + command_input_history_expiration_time_get( + acting_player->ent, + input_event.time + ); + } + } + + } + + bothkeys |= acting_player->keys; + bothnewkeys |= acting_player->newkeys; + + } + +} + +void execute_keyscripts() +{ + int p; + for(p = 0; p < levelsets[current_set].maxplayers; p++) + { + if(!_pause && (level || check_in_screen()) && (player[p].newkeys || (keyscriptrate && player[p].keys) || player[p].releasekeys)) + { + if(level) + { + execute_level_key_script(p); + execute_entity_key_script(player[p].ent); + } + execute_key_script(p); + execute_key_script_all(p); + } + } +} + +void execute_updatescripts() +{ + if(Script_IsInitialized(&update_script)) + { + Script_Execute(&(update_script)); + } + if(level && Script_IsInitialized(&(level->update_script))) + { + Script_Execute(&(level->update_script)); + } +} + +void execute_updatedscripts() +{ + if(Script_IsInitialized(&updated_script)) + { + Script_Execute(&(updated_script)); + } + if(level && Script_IsInitialized(&(level->updated_script))) + { + Script_Execute(&(level->updated_script)); + } +} + +/* +* Caskey, Damon V. +* 2026-08-21 +* +* Execute global and level scripts immediately before an +* engine logical clock tick is processed. +*/ +void execute_updatelogicscripts() +{ + if(Script_IsInitialized(&update_logic_script)) + { + Script_Execute(&update_logic_script); + } + if(level && Script_IsInitialized(&level->update_logic_script)) + { + Script_Execute(&level->update_logic_script); + } +} + +/* +* Caskey, Damon V. +* 2026-08-21 +* +* Execute global and level scripts after an engine logical +* clock tick is processed, before its clock value advances. +*/ +void execute_updatedlogicscripts() +{ + if(Script_IsInitialized(&updated_logic_script)) + { + Script_Execute(&updated_logic_script); + } + if(level && Script_IsInitialized(&level->updated_logic_script)) + { + Script_Execute(&level->updated_logic_script); + } +} + +void draw_textobjs() +{ + int i; + s_textobj *textobj; + if(!level) + { + return; + } + for(i = 0; i < level->numtextobjs ; i++) + { + textobj = level->textobjs + i; + + if(textobj->time && textobj->time <= _time) //If a time was set and passed, remove the text object. + { + level->textobjs[i].time = 0; + level->textobjs[i].position.x = 0; + level->textobjs[i].position.y = 0; + level->textobjs[i].font = 0; + level->textobjs[i].position.z = 0; + if(level->textobjs[i].text) + { + free(level->textobjs[i].text); + level->textobjs[i].text = NULL; + } + } + else + { + if(textobj->text) + { + font_print_length( + textobj->position.x, + textobj->position.y, + textobj->font, + textobj->position.z, + textobj->text, + strlen(textobj->text) + ); + } + } + } +} + +int recordInputs() +{ + int p = 0; + RecKeys reckey; + uint64_t window = 4096; + u32 max_rec_time = global_config.game_speed*60*10; // protection + + if(playrecstatus->status != A_REC_REC) return 0; + if ( !playrecstatus->begin ) + { + playrecstatus->starttime = _time; + playrecstatus->synctime = 0; + if (playrecstatus->buffer) + { + free(playrecstatus->buffer); + playrecstatus->buffer = NULL; + } + playrecstatus->buffer = (RecKeys*)calloc(window+playrecstatus->synctime*sizeof(RecKeys),sizeof(RecKeys)); + if (playrecstatus->buffer == NULL) + { + printf("Error to allocate buffer in record inputs mode.\n"); + return 0; + } + playrecstatus->begin = 1; + playrecstatus->seed = getseed(); + playrecstatus->cseed = _time; + srand(_time); + playrecstatus->ticks = timer_gettick(); + } + else + { + if ( playrecstatus->synctime%window >= window-2 ) // last is NULL bytes + { + playrecstatus->buffer = (RecKeys*)realloc(playrecstatus->buffer,sizeof(RecKeys)*((playrecstatus->synctime/window+1)*window+window)); + if (playrecstatus->buffer == NULL) + { + printf("Error to allocate buffer in record inputs mode.\n"); + return 0; + } else memset(playrecstatus->buffer+(playrecstatus->synctime+1),0,(playrecstatus->synctime/window+1)*window+window-(playrecstatus->synctime+1)-1); // -2 because -1 is to 0 to size-1 + } + } + + // now rec! + if(playrecstatus->buffer && playrecstatus->begin) + { + for ( p = 0; p < MAX_PLAYERS; p++ ) + { + reckey.keys[p] = player[p].keys; + reckey.newkeys[p] = player[p].newkeys; + reckey.releasekeys[p] = player[p].releasekeys; + reckey.playkeys[p] = player[p].playkeys; + } + reckey.time = _time; + reckey.interval = interval; + reckey.synctime = playrecstatus->synctime; + //reckey.seed = getseed(); + memcpy( &playrecstatus->buffer[playrecstatus->synctime], &reckey, sizeof(reckey) ); + } + + if ( _time >= max_rec_time ) stopRecordInputs(); // safe + if(playrecstatus->status == A_REC_REC) ++playrecstatus->synctime; + + //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime); + //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK); + + return 1; +} + +int playRecordedInputs() +{ + int p = 0; + RecKeys reckey; + char path[MAX_ARG_LEN + 1]; + size_t filesize = 0; + char header[6]; + + if(playrecstatus->status != A_REC_PLAY) return 0; + if ( !playrecstatus->begin ) //time >= playrecstatus->starttime && + { + if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0); + else strcpy(path,playrecstatus->path); + if ( path[strlen(path)-1] != '/' ) strcpy(path,"/"); + + if (playrecstatus->buffer) + { + free(playrecstatus->buffer); + playrecstatus->buffer = NULL; + } + if(playrecstatus->handle) + { + fclose(playrecstatus->handle); + } + + playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "rb+"); + if(playrecstatus->handle) + { + fseek(playrecstatus->handle, 0L, SEEK_END); + filesize = ftell(playrecstatus->handle); + fseek(playrecstatus->handle, 0L, SEEK_SET); // or rewind(playrecstatus->handle) + + if (filesize <= 0) + { + printf("Empty recorded inputs file.\n"); + return 0; + } + playrecstatus->buffer = (RecKeys*)calloc(1,filesize+1); + if(!playrecstatus->buffer) + { + printf("Error to allocate buffer for recorded inputs.\n"); + return 0; + } + } else + { + printf("Error to open recorded inputs file.\n"); + return 0; + } + fread(&header, 6, 1, playrecstatus->handle); + fread(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle); + fread(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle); + fread(&playrecstatus->totsynctime, sizeof(u32), 1, playrecstatus->handle); + fread(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle); + fread(&playrecstatus->seed, sizeof(uint64_t), 1, playrecstatus->handle); + fread(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle); + fread(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->endtime+1), 1, playrecstatus->handle); + + // sync at start time + _time = playrecstatus->starttime; + playrecstatus->synctime = 0; + playrecstatus->begin = 1; + srand(playrecstatus->cseed); + srand32(playrecstatus->seed); + //set_ticks(timer_gettick()-playrecstatus->ticks); + } + + // now play! + if(playrecstatus->buffer && playrecstatus->begin && playrecstatus->synctime < playrecstatus->totsynctime) + { + memcpy( &reckey, &playrecstatus->buffer[playrecstatus->synctime], sizeof(reckey) ); + + if( _time != reckey.time ) + { + u32 nextsynctime = reckey.synctime; + + //time = (u32)reckey.time; + printf("Play recorded inputs: Out of sync! Time: %d, RecTime: %d\n",time,reckey.time); + /*if ( interval != reckey.interval ) + { + //interval = (u32)reckey.interval; + printf("Play recorded inputs: Out of sync! Interval: %d, RecInterval: %d\n",interval,reckey.interval); + }*/ + + while( _time > reckey.time && nextsynctime > 0 ) { + memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) ); + } + _time = reckey.time; + + while( _time < reckey.time && nextsynctime < playrecstatus->totsynctime ) { + memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) ); + } + _time = reckey.time; + } + + if ( _time == reckey.time ) + { + if ( playrecstatus->synctime != reckey.synctime ) + { + u32 nextsynctime = reckey.synctime; + + printf("Play recorded inputs: Out of sync! SyncTime: %d, RecSyncTime: %d\n",playrecstatus->synctime,reckey.synctime); + + while( playrecstatus->synctime > reckey.synctime && nextsynctime > 0 ) { + memcpy( &reckey, &playrecstatus->buffer[--nextsynctime], sizeof(reckey) ); + } + playrecstatus->synctime = reckey.synctime; + + while( playrecstatus->synctime < reckey.synctime && nextsynctime < playrecstatus->totsynctime ) { + memcpy( &reckey, &playrecstatus->buffer[++nextsynctime], sizeof(reckey) ); + } + playrecstatus->synctime = reckey.synctime; + } + + if ( interval != reckey.interval ) interval = reckey.interval; + //srand32(reckey.seed); + for ( p = 0; p < MAX_PLAYERS; p++ ) + { + player[p].keys = reckey.keys[p]; + player[p].newkeys = reckey.newkeys[p]; + player[p].releasekeys = reckey.releasekeys[p]; + player[p].playkeys = reckey.playkeys[p]; + } + //inputrefresh(playrecstatus->status); + } + } + + //debug_printf("synctim: %d totsync: %d status:%d",playrecstatus->synctime,playrecstatus->totsynctime,playrecstatus->status); + if(playrecstatus->status == A_REC_PLAY) ++playrecstatus->synctime; + if ( playrecstatus->synctime >= playrecstatus->totsynctime || _time >= playrecstatus->endtime ) stopRecordInputs(); + + //debug_printf("time: %d sync: %d",(u32)time,(u32)playrecstatus->synctime); + //debug_printf("keys: %d",player[0].releasekeys&FLAG_ATTACK); + + return 1; +} + +int stopRecordInputs() +{ + if(playrecstatus) + { + switch(playrecstatus->status) + { + case A_REC_REC: + { + char path[MAX_ARG_LEN + 1]; + char header[6] = "INP10"; + + if ( strlen(playrecstatus->path) <= 0 ) getBasePath(path, "Saves", 0); + else strcpy(path,playrecstatus->path); + if ( path[strlen(path)-1] != '/' ) strcpy(path,"/"); + + if (playrecstatus->buffer) + { + playrecstatus->handle = fopen(strcat(path,playrecstatus->filename), "wb+"); + if(playrecstatus->handle) + { + playrecstatus->endtime = _time; + if (playrecstatus->synctime < 2) playrecstatus->synctime = 2; + else playrecstatus->synctime -= 2; + + fwrite(header, 6, 1, playrecstatus->handle); + fwrite(&playrecstatus->starttime, sizeof(u32), 1, playrecstatus->handle); + fwrite(&playrecstatus->endtime, sizeof(u32), 1, playrecstatus->handle); + fwrite(&playrecstatus->synctime, sizeof(u32), 1, playrecstatus->handle); + fwrite(&playrecstatus->cseed, sizeof(u32), 1, playrecstatus->handle); + fwrite(&playrecstatus->seed, sizeof(uint64_t), 1, playrecstatus->handle); + fwrite(&playrecstatus->ticks, sizeof(unsigned), 1, playrecstatus->handle); + fwrite(playrecstatus->buffer, sizeof(RecKeys)*(playrecstatus->synctime+1), 1, playrecstatus->handle); + fflush(playrecstatus->handle); // safe + fclose(playrecstatus->handle); + } else return 0; + + free(playrecstatus->buffer); + playrecstatus->buffer = NULL; + } else return 0; + break; + } + case A_REC_PLAY: + { + if(playrecstatus->handle) + { + if (playrecstatus->handle) fclose(playrecstatus->handle); + else return 0; + } + break; + } + case A_REC_STOP: + { + if(playrecstatus->handle) + { + if (playrecstatus->handle) fclose(playrecstatus->handle); + else return 0; + } + break; + } + } + + playrecstatus->status = A_REC_STOP; + playrecstatus->begin = 0; + playrecstatus->synctime = 0; + freeRecordedInputs(); + } return 0; + + return 1; +} + +int freeRecordedInputs() +{ + playrecstatus->status = A_REC_STOP; + if(playrecstatus->handle) fclose(playrecstatus->handle); + if(playrecstatus->buffer) + { + free(playrecstatus->buffer); + playrecstatus->buffer = NULL; + return 1; + } + + return 0; +} + +a_playrecstatus* init_input_recorder() +{ + playrecstatus = (a_playrecstatus*)calloc(1,sizeof(*playrecstatus)); + + return playrecstatus; +} + +void free_input_recorder() +{ + if(playrecstatus) + { + if(playrecstatus->buffer) + { + if(playrecstatus->handle) fclose(playrecstatus->handle); + free(playrecstatus->buffer); + playrecstatus->buffer = NULL; + } + free(playrecstatus); + playrecstatus = NULL; + } +} + +void update(int ingame, int usevwait) +{ + int i = 0; + int p_keys = 0; + +#if SDL + if (savedata.fpslimit == 1) // vsync enabled + { + // To reduce input latency, wait until the last 4 ms (4000 us) of the current + // frame to read inputs or do anything else. We can get away with this because + // the CPUs of all modern computers - even phones and low-end, outdated PCs - + // are complete overkill for OpenBOR's needs. + s64 target_time = timer_uticks() + 1000000/video_current_refresh_rate() - 4000; + u64 current_time = timer_uticks(); + while (current_time < target_time) + { + usleep(target_time - current_time); + current_time = timer_uticks(); + } + } +#endif + + getinterval(); + if(playrecstatus->status == A_REC_PLAY && !_pause && level) if ( !playRecordedInputs() ) stopRecordInputs(); + inputrefresh(playrecstatus->status); +#ifdef WEBM + movie_playback_update( + (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)) != 0 + ); +#endif + if(playrecstatus->status == A_REC_REC && !_pause && level) if ( !recordInputs() ) stopRecordInputs(); + + if ((!_pause && ingame == 1) || alwaysupdate) + { + execute_updatescripts(); + } + + newtime = 0; + if(!_pause) + { + if(ingame == 1 || check_in_screen()) + { + execute_keyscripts(); + } + + if((level_completed && level->boss_slow == BOSS_SLOW_ON && !tospeedup) || slowmotion.toggle > SLOW_MOTION_OFF) + { + if(slowmotion.duration == slowmotion.counter) + { + newtime = _time + interval; + } + } + else + { + newtime = _time + interval; + } + + slowmotion.counter++; + if(slowmotion.counter == (slowmotion.duration + 1)) + { + slowmotion.counter = 0; + if(slowmotion.toggle > SLOW_MOTION_ON) + { + slowmotion.duration = slowmotion.toggle; + } + } + if(newtime > _time + 100) + { + newtime = _time + 100; + } + + while(_time < newtime) + { + if(ingame == 1 || alwaysupdate) + { + execute_updatelogicscripts(); + } + + if(ingame == 1) + { + update_scroller(); + if(!freezeall) + { + int all_p_alive = 0; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if (!player[i].ent) ++all_p_alive; + } + all_p_alive = (all_p_alive >= MAX_PLAYERS) ? 1 : 0; + + if(level->settime > 0 || (level->type != 2 && all_p_alive)) + //if(level->settime > 0 || (level->type != 2 && !player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent)) + { + int all_p_nojoin = 0, all_p_nocredits = 0; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if (!player[i].joining) ++all_p_nojoin; + } + all_p_nojoin = (all_p_nojoin >= MAX_PLAYERS) ? 1 : 0; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].credits < 1) ++all_p_nocredits; + } + all_p_nocredits = (all_p_nocredits >= MAX_PLAYERS) ? 1 : 0; + + if(timeleft > 0) + { + --timeleft; + } + else if((level->settime > 0 && all_p_nojoin) || + ( ((!noshare && credits < 1) || (noshare && all_p_nocredits)) + && all_p_nojoin ) + ) + { + time_over(); + } + } + } + update_scrolled_bg(); + if(level->type != 2) + { + updatestatus(); + } + } + if(ingame == 1 || check_in_screen()) + { + update_ents(); + } + + if(ingame == 1 || alwaysupdate) + { + execute_updatedlogicscripts(); + } + + ++_time; + } + + } + + /************ gfx queueing ************/ + + clearscreen(vscreen); + + if(ingame == 1 && !_pause) + { + draw_scrolled_bg(); + if(level->type != 2) + { + predrawstatus(); + } + if(level->type != 2) + { + drawstatus(); + } + draw_textobjs(); + } + + if(!ingame) + { + if(background) + { + spriteq_add_screen(0, 0, MIN_INT, background, NULL, 0); + } + } + + // entity sprites queueing + if(ingame == 1 || check_in_screen()) + if(!_pause) + { + display_ents(); + } + + /************ updated script ************/ + if(ingame == 1 || alwaysupdate) + { + execute_updatedscripts(); + } + + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].ent && (player[i].newkeys & FLAG_START)) + { + p_keys = 1; + break; + } + } + + // 2011/10/22 UT: move pause menu logic here + /*if(ingame == 1 && !_pause && !nopause && + ((player[0].ent && (player[0].newkeys & FLAG_START)) || + (player[1].ent && (player[1].newkeys & FLAG_START)) || + (player[2].ent && (player[2].newkeys & FLAG_START)) || + (player[3].ent && (player[3].newkeys & FLAG_START))) + )*/ + if(ingame == 1 && !_pause && !nopause && p_keys) + { + if ( !(goto_mainmenu_flag&1) ) + { + sound_pause_music(1); + sound_pause_sample(1); + sound_play_sample(global_sample_list.pause, 0, savedata.effectvol, savedata.effectvol, 100); + pausemenu(); + return; + } + } + if( ingame == 1 && (goto_mainmenu_flag&1) ) + { + backto_mainmenu(); + return; + } + + /********** update screen **************/ + + spriteq_draw(vscreen, 0, MIN_INT, MAX_INT, 0, 0); // notice, always draw sprites at the very end of other methods + + if(_pause != 2 && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT)) + { + screenshot(vscreen, getpal, 1); + } + + // Debug stuff, should not appear on screenshot + if(debug_time == 0xFFFFFFFF) + { + debug_time = _time + global_config.game_speed * 5; + } + if(_time < debug_time && debug_msg[0]) + { + if( debug_xy_msg.x >= 0 && debug_xy_msg.y >= 0 ) + { + if ( debug_xy_msg.font_index < 0 ) debug_xy_msg.font_index = 0; + screen_printf(vscreen, debug_xy_msg.x, debug_xy_msg.y, debug_xy_msg.font_index, debug_msg); + } else screen_printf(vscreen, 0, videomodes.vRes-fontheight(0), 0, debug_msg); + } + else + { + debug_msg[0] = 0; + debug_xy_msg.x = -1; + debug_xy_msg.y = -1; +#ifdef DEBUG_MODE + if(level->pos) + { + debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", level->pos, level->width, current_spawn, level->quake, gfx_y_offset); + } +#endif + } + + if(usevwait) + { + vga_vwait(); + } + video_copy_screen(vscreen); + spriteq_clear(); + + check_music(); + sound_update_music(); +} + + + + +// ---------------------------------------------------------------------- +/* Plombo 9/4/2010: New function that can use brightness/gamma correction + * independent from the global palette on platforms where it's available. + * Hardware accelerated brightness/gamma correction is available on OpenGL + * platforms using GLSL. Returns 1 on success, 0 on error. + */ +int set_color_correction(int gm, int br) +{ + video_set_color_correction(gm, br); + return 1; +} + +// Simple palette fade / vscreen fade +void fade_out(int type, int speed) +{ + int i, j = 0; + int b, g = 0; + u32 interval = 0; + int current = speed ? speed : fade; + s_screen *fbuffer = NULL; + s_drawmethod dm = plainmethod; + dm.alpha = BLEND_MODE_AVERAGE; + + for(i = 0, j = 0; j < 64; ) + { + while(j <= i && j < 64) + { + if(!type || type == 1) + { + b = ((savedata.brightness + 256) * (64 - j) / 64) - 256; + g = 256 - ((savedata.gamma + 256) * (64 - j) / 64); + vga_vwait(); + if(!set_color_correction(g, b)) + { + if(!fbuffer) + { + fbuffer = allocscreen(vscreen->width, vscreen->height, vscreen->pixelformat); + copyscreen(fbuffer, vscreen); + } + //255 + alpha BLEND_MODE_AVERAGE is actually half blend, so use 254 instead + dm.channelr = dm.channelg = dm.channelb = 254 * (64 - j) / 64; + clearscreen(vscreen); + putscreen(vscreen, fbuffer, 0, 0, &dm); + } + } + j++; + if(!type || type == 1) + { + video_copy_screen(vscreen); + } + } + if(!type || type == 2) + { + sound_update_music(); + if(!musicoverlap) + { + sound_volume_music(savedata.musicvol * (64 - j) / 64, savedata.musicvol * (64 - j) / 64); + } + } + interval = timer_getinterval(current); + if(interval > current) + { + interval = current / 60; + } + if(interval > current / 4) + { + interval = current / 4; + } + i += interval; + } + + if(!type || type == 2) + { + if(!musicoverlap) + { + sound_close_music(); + } + } + + if(!type || type == 1) + { + clearscreen(vscreen); + video_copy_screen(vscreen); + vga_vwait(); + //the black screen, so we return to normal palette + set_color_correction(savedata.gamma, savedata.brightness); + } + + if(fbuffer) + { + freescreen(&fbuffer); + } +} + + + +void apply_controls() +{ + int p; + + for(p = 0; p < MAX_PLAYERS; p++) + { + control_setkey(playercontrolpointers[p], FLAG_ESC, CONTROL_ESC); + control_setkey(playercontrolpointers[p], FLAG_MOVEUP, savedata.keys[p][SDID_MOVEUP]); + control_setkey(playercontrolpointers[p], FLAG_MOVEDOWN, savedata.keys[p][SDID_MOVEDOWN]); + control_setkey(playercontrolpointers[p], FLAG_MOVELEFT, savedata.keys[p][SDID_MOVELEFT]); + control_setkey(playercontrolpointers[p], FLAG_MOVERIGHT, savedata.keys[p][SDID_MOVERIGHT]); + control_setkey(playercontrolpointers[p], FLAG_ATTACK, savedata.keys[p][SDID_ATTACK]); + control_setkey(playercontrolpointers[p], FLAG_ATTACK2, savedata.keys[p][SDID_ATTACK2]); + control_setkey(playercontrolpointers[p], FLAG_ATTACK3, savedata.keys[p][SDID_ATTACK3]); + control_setkey(playercontrolpointers[p], FLAG_ATTACK4, savedata.keys[p][SDID_ATTACK4]); + control_setkey(playercontrolpointers[p], FLAG_JUMP, savedata.keys[p][SDID_JUMP]); + control_setkey(playercontrolpointers[p], FLAG_SPECIAL, savedata.keys[p][SDID_SPECIAL]); + control_setkey(playercontrolpointers[p], FLAG_START, savedata.keys[p][SDID_START]); + control_setkey(playercontrolpointers[p], FLAG_SCREENSHOT, savedata.keys[p][SDID_SCREENSHOT]); + } +} + + + +// ---------------------------------------------------------------------- + +void display_credits() +{ + u32 finishtime = _time + 10 * global_config.game_speed; + int done = 0; + int s = videomodes.vShift / 2 + 3; + int v = (videomodes.vRes - videomodes.vShift) / 24; + int m = 0; + int h = videomodes.hRes / 2; + int col1 = h - fontmonowidth(0) * 16; + int col2 = h + fontmonowidth(0) * 4; + + if(savedata.logo != 1) + { + return; + } + fade_out(0, 0); + + unload_level(); + + bothnewkeys = 0; + + while(!done) + { + m = 2; + + font_printf(_strmidx(2, "Credits"), s, 2, 0, "Credits"); + + font_printf(_strmidx(1, "OpenBOR"), s + v * m, 1, 0, "OpenBOR"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Caskey, Damon V."); + font_printf(col2, s + v * m, 0, 0, "Project Lead"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Msmalik681"); + font_printf(col2, s + v * m, 0, 0, "Developer"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Kratus"); + font_printf(col2, s + v * m, 0, 0, "Developer"); ++m; + + font_printf(_strmidx(1, "Former Staff"), s + v * m, 1, 0, "Former Staff"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Fightn Words"); + font_printf(col2, s + v * m, 0, 0, "Fugue"); ++m; + + font_printf(col1, s + v * m, 0, 0, "KBAndressen"); + font_printf(col2, s + v * m, 0, 0, "Kirby"); ++m; + + font_printf(col1, s + v * m, 0, 0, "LordBall"); + font_printf(col2, s + v * m, 0, 0, "Orochi_X"); ++m; + + font_printf(col1, s + v * m, 0, 0, "SX"); + font_printf(col2, s + v * m, 0, 0, "Tails"); ++m; + + font_printf(col1, s + v * m, 0, 0, "uTunnels"); + font_printf(col2, s + v * m, 0, 0, "White Dragon"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Plombo"); ++m; + + font_printf(_strmidx(1, "Ports"), s + v * m, 1, 0, "Ports"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Linux/OSX"); + font_printf(col2, s + v * m, 0, 0, "SX"); ++m; + + font_printf(col1, s + v * m, 0, 0, "Android"); + font_printf(col2, s + v * m, 0, 0, "CRxTRDude, Plombo,"); ++m; + font_printf(col2, s + v * m, 0, 0, "uTunnels, Msmalik681"); ++m; + font_printf(col2, s + v * m, 0, 0, "White Dragon"); ++m; + + update(2, 0); + + done |= (_time > finishtime); + done |= (bothnewkeys & (FLAG_START + FLAG_ESC)); + } + fade = 75; + fade_out(0, 0); +} + + +void borShutdown(int status, const char *msg, ...) +{ + va_list arglist; + va_list output_arguments; + int i; + + static int shuttingdown = 0; + + if(shuttingdown) + { + return; + } + + shuttingdown = 1; + va_start(arglist, msg); + + //printf("savedata.logo %d\n", savedata.logo); + + if(!disablelog) + { + switch(status) + { + case 0: + printf("\n************ Shutting Down ************\n\n"); + break; + default: + printf("\n********** An Error Occurred **********" + "\n* Shutting Down *\n\n"); + break; + } + } + + if(!disablelog) + { + va_copy(output_arguments, arglist); + writeToLogFileV(msg, output_arguments); + va_end(output_arguments); + } + + + getRamStatus(BYTES); + savesettings(); + + /* entry point for the engine credits screen. */ + //screen_status |= IN_SCREEN_ENGINE_CREDIT; + + // if(status != 2) + // { + // display_credits(); + // } + + if(startup_done) + { + screen_status &= ~IN_SCREEN_ENGINE_CREDIT; + term_videomodes(); + } + + if(!disablelog) + { + printf("Release level data"); + } + if (startup_done) + { + unload_levelorder(); + } + if(!disablelog) + { + printf("...........\n"); + } + if(startup_done) + { + unload_level(); + } + if(!disablelog) + { + printf("Done!\n\n"); + } + + if(!disablelog) + { + printf("Release graphics data"); + } + if(!disablelog) + { + printf(".."); + } + if(startup_done) + { + freescreen(&vscreen); // allocated by init_videomodes + } + if(startup_done && pixelformat == PIXEL_x8) for(i = 0; i < MAX_BLENDINGS; i++) + { + free(blendtables[i]); + } + if(!disablelog) + { + printf(".."); + } + if(startup_done) + { + freescreen(&background); + } + if(!disablelog) + { + printf(".."); + } +#ifdef CACHE_BACKGROUNDS + if(startup_done) for(i = 0; i < MAX_CACHED_BACKGROUNDS; i++) + { + freescreen(&bg_cache[i]); + } + if(!disablelog) + { + printf(".."); + } +#endif + if(startup_done) + { + freesprites(); + } + if(!disablelog) + { + printf(".."); + } + if(startup_done) + { + unload_all_fonts(); + } + if(!disablelog) + { + printf("\tDone!\n"); + } + + + if(!disablelog) + { + printf("Release game data............\n\n"); + } + + if(startup_done) + { + free_ents(); + } + if(startup_done) + { + free_models(); + } + if(startup_done) + { + free_modelcache(); + } + if(startup_done) + { + clear_scripts(); + } + if(!disablelog) + { + printf("\nRelease game data............\tDone!\n"); + } + + if(!disablelog) + { + printf("Release timer................"); + } + if(startup_done) + { + borTimerExit(); + } + if(!disablelog) + { + printf("\tDone!\n"); + } + + if(!disablelog) + { + printf("Release input hardware......."); + } + if(startup_done) + { + control_exit(); + } + if(!disablelog) + { + printf("\tDone!\n"); + } + + if(!disablelog) + { + printf("Release sound system........."); + } + if(startup_done) + { + sound_exit(); + } + if(!disablelog) + { + printf("\tDone!\n"); + } + + if(!disablelog) + { + printf("Release FileCaching System..."); + } + if(startup_done) + { + pak_term(); + } + if(!disablelog) + { + printf("\tDone!\n"); + } + + if(modelcmdlist) + { + freeCommandList(modelcmdlist); // moved here because list is not initialized if shutdown is initiated from inside the menu + } + if(modelstxtcmdlist) + { + freeCommandList(modelstxtcmdlist); + } + if(levelcmdlist) + { + freeCommandList(levelcmdlist); + } + if(levelordercmdlist) + { + freeCommandList(levelordercmdlist); + } + + freeModelList(); + clear_saved_allowselect_arguments(); + free(savelevel_allowselect_args); + savelevel_allowselect_args = NULL; + if(savelevel) + { + free(savelevel); + savelevel = NULL; + } + savelevel_count = 0; + free(allowselect_args); + allowselect_args = NULL; + freefilenamecache(); + ob_termtrans(); + + // free input recorder + free_input_recorder(); + + if(!disablelog) + { + printf("\n**************** Done *****************\n\n"); + } + + if(!disablelog) + { + va_copy(output_arguments, arglist); + writeToLogFileV(msg, output_arguments); + va_end(output_arguments); + } + + va_end(arglist); + shuttingdown = 0; + borExit(status); +} + + +void guistartup() +{ + int i; + + if(!font_load(0, "menu/font1", packfile, 0)) + { + borShutdown(1, "Unable to load font #1!\n"); + } + if(!font_load(1, "menu/font2", packfile, 0)) + { + borShutdown(1, "Unable to load font #2!\n"); + } + if(!font_load(2, "menu/font3", packfile, 0)) + { + borShutdown(1, "Unable to load font #3!\n"); + } + + + borTimerInit(); + + control_init(2); + apply_controls(); + + init_videomodes(0); + if(!video_set_mode(videomodes)) + { + borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes); + } + + for(i = 0; i < 256; i++) + { + neontable[i] = i; + } +} + + +void startup() +{ + int i; + + printf("FileCaching System Init.......\t"); + if(pak_init()) + { + printf("Enabled\n"); + } + else + { + printf("Disabled\n"); + } + + loadHighScoreFile(); + clearSavedGame(); + + init_videomodes(1); + if(!video_set_mode(videomodes)) + { + borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes); + } + + printf("Timer init...................\t"); + borTimerInit(); + printf("Done!\n"); + + printf("Initialize Sound.............\t"); + if(sound_init()) + { + if(load_special_sounds()) + { + printf("Done!\n"); + } + else + { + printf("\n"); + } + if(!sound_start_playback()) + { + printf("Warning: can't play sound!\n"); + } + SB_setvolume(SB_MASTERVOL, savedata.soundvol); + SB_setvolume(SB_VOICEVOL, savedata.soundvol); + } + else + { + borShutdown(1, "Unable to Initialize Sound.\n"); + } + + // init. input recorder + init_input_recorder(); + + printf("Loading fonts................\t"); + load_all_fonts(); + printf("Done!\n"); + + printf("Loading sprites..............\t"); + load_special_sprites(); + printf("Done!\n"); + + printf("Loading level order..........\t"); + load_levelorder(); + printf("Done!\n"); + + printf("Loading model constants......\t"); + load_model_constants(); + printf("Done!\n"); + + printf("Loading script settings......\t"); + load_script_setting(); + printf("Done!\n"); + + printf("Loading scripts..............\t"); + load_scripts(); + printf("Done!\n"); + + printf("Loading models...............\n\n"); + load_models(); + + printf("Object engine init...........\t"); + if(!alloc_ents()) + { + borShutdown(1, "Not enough memory for game objects!\n"); + } + printf("Done!\n"); + + printf("Loading menu.txt.............\t"); + load_menu_txt(); + printf("Done!\n"); + + /* + Kratus (01-2024) Moved the translation and menu functions to the end of the engine "startup" function, + but before the "control init" function (reverted the font function to load before scripts) + */ + printf("Loading translation..........\t"); + ob_inittrans(); + printf("Done!\n"); + + printf("Input init...................\t"); + control_init(savedata.usejoy); + apply_controls(); + printf("Done!\n"); + +#ifdef CACHE_BACKGROUNDS + printf("Caching backgrounds..........\t"); + cache_all_backgrounds(); + printf("Done!\n"); +#endif + + printf("Create blending tables.......\t"); + if(pixelformat == PIXEL_x8) + { + create_blend_tables_x8(blendtables); + } + + for(i = 0; i < MAX_PAL_SIZE / 4; i++) + { + neontable[i] = i; + } + printf("Done!\n"); + + if(savedata.logo++ > 10) + { + savedata.logo = 0; + } + + printf("Save settings so far.........\t"); + savesettings(); + printf("Done!\n"); + + startup_done = 1; + + printf("\n\n"); + +} + + + +// ---------------------------------------------------------------------------- + + +// Returns 0 on error, -1 on escape +int playgif(char *filename, int x, int y, int noskip) +{ + anigif_info *info = calloc(1, sizeof(*info)); + s_screen *backbuffer = NULL; + s_screen *tempbg = background; + int result = 1; + u32 synctosound; + u32 lasttime; + u32 milliseconds; + u32 temptime, tempnewtime; // temporary patch for ingame gif play + + synctosound = (sound_getinterval() != 0xFFFFFFFF); + temptime = _time; + tempnewtime = newtime; + _time = 0; + milliseconds = 0; + lasttime = 0; + background = NULL; + + if(!(result = anigif_open(filename, packfile, info))) + { + goto playgif_end; + } + + while(!info->done) + { + if(milliseconds >= info->info[0].nextframe) + { + anigif_decode_frame(info); + } + if(!(backbuffer = anigif_getbuffer(info))) + { + break; + } + spriteq_add_screen(x, y, 0, backbuffer, NULL, 0); + if(info->frame == 0) + { + vga_vwait(); + update(0, 0); + } + else + { + update(0, 1); + } + + if(synctosound) + { + milliseconds += sound_getinterval(); + } + else + { + milliseconds += (_time - lasttime) * 1000 / global_config.game_speed; + } + + lasttime = _time; + + if(!noskip && (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON))) + { + result = -1; + break; + } + } + + +playgif_end: + anigif_close(info); + free(info); + _time = temptime; + newtime = tempnewtime; + background = tempbg; + //standard_palette(1); //Kratus (01-2024) Disabled to fix the fps drop issue + + if(0 == result) + { + printf("\nWarning, an error occurred while playing animated gif file '%s'.\n", filename); + } + return result; + +} + + +#ifdef WEBM +/* +* Caskey, Damon V. +* 2026-08-12 +* +* Preserve blocking playwebm() behavior as a wrapper around +* the reusable movie APIs. Return 1 on completion, 0 on error, +* or -1 when creator-enabled input interrupts playback. +*/ +int playwebm(const char *path, int noskip) +{ + int retval = 1; + int source_id = -1; + s_movie_playback *playback = NULL; + s_screen *screenshot_frame = NULL; + + movie_playback_stop_all(); + source_id = movie_source_load(path, MOVIE_LOADING_STREAM); + if(source_id < 0) { + return 0; + } + playback = movie_playback_play( + source_id, + MOVIE_CHANNEL_AUTO, + savedata.musicvol, + true + ); + if(!playback) { + retval = 0; + goto quit; + } + if(!movie_playback_set_interrupt(playback, !noskip)) { + retval = 0; + goto quit; + } + + /* Legacy playback uses the WebM display size and hardware YUV output. */ + movie_playback_set_width(playback, MOVIE_SIZE_NATIVE); + movie_playback_set_height(playback, MOVIE_SIZE_NATIVE); + + while(playback->active) { + int interrupt_requested; + int present_frame; + + inputrefresh(playrecstatus->status); + interrupt_requested = + (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)) != 0; + if(interrupt_requested && playback->interrupt) { + retval = -1; + } + movie_playback_update(interrupt_requested); + if(!playback->active) { + break; + } + present_frame = playback->frame_dirty; + if(present_frame) { + if(!movie_playback_draw_to_yuv(playback->index)) { + retval = 0; + break; + } + } + if(playback->current_frame && + !noscreenshot && + (bothnewkeys & FLAG_SCREENSHOT)) { + if(!screenshot_frame || + screenshot_frame->width != playback->current_frame->width || + screenshot_frame->height != playback->current_frame->height) { + if(screenshot_frame) { + freescreen(&screenshot_frame); + } + screenshot_frame = allocscreen( + playback->current_frame->width, + playback->current_frame->height, + PIXEL_32 + ); + } + if(screenshot_frame) { + yuv_to_rgb(playback->current_frame, screenshot_frame); + screenshot(screenshot_frame, NULL, 0); + } + } + usleep(1000); + } + +quit: + if(screenshot_frame) { + freescreen(&screenshot_frame); + } + if(playback && playback->failed && retval > 0) { + retval = 0; + } + if(playback && playback->active) { + movie_playback_stop(playback); + } + while(source_id >= 0 && !movie_source_unload(source_id)) { + movie_playback_update(0); + usleep(1000); + } + return retval; +} +#endif + + + +void playscene(char *filename) +{ + char *buf; + size_t size; + int pos; + char *command = NULL; + char videofile[MAX_BUFFER_LEN]; + int x = 0, y = 0, skipone = 0, noskip = 0, i; + int closing = 0, status; + + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + + // Read file + if(buffer_pakfile(filename, &buf, &size) != 1) + { + return; + } + + currentScene = filename; + + // Now interpret the contents of buf line by line + pos = 0; + while(buf[pos]) + { + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + if(command[0]) + { + if(!closing && stricmp(command, "music") == 0) + { + music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3))); + } + else if(!closing && stricmp(command, "animation") == 0) + { + strcpy(videofile, GET_ARG(1)); + x = GET_INT_ARG(2); + y = GET_INT_ARG(3); + skipone = GET_INT_ARG(4); + noskip = GET_INT_ARG(5); + status = playgif(videofile, x, y, noskip); + if(status == -1 && !skipone) + { + closing = 1; + } + } + else if(!closing && stricmp(command, "video") == 0) + { +#ifdef WEBM + strcpy(videofile, GET_ARG(1)); + skipone = GET_INT_ARG(2); + noskip = GET_INT_ARG(3); + status = playwebm(videofile, noskip); + if(status == -1 && !skipone) + { + closing = 1; + } + else if(status == 0) + { + printf("An error occurred when trying to play the video %s\n", videofile); + } +#else + printf("Skipping video %s; WebM playback not supported on this platform\n"); +#endif + } + else if(stricmp(command, "silence") == 0) + { + sound_close_music(); + } + } + // Go to next non-blank line + pos += getNewLineStart(buf + pos); + } + if(buf != NULL) + { + free(buf); + buf = NULL; + } + currentScene = NULL; + for(i = 0; i < MAX_PLAYERS; i++) + { + player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0; + } +} + + + + +// ---------------------------------------------------------------------------- + + + + +void gameover() +{ + int done = 0; + char tmpBuff[MAX_BUFFER_LEN] = {""}; + + music("data/music/gameover", 0, 0); + + _time = 0; + screen_status |= IN_SCREEN_GAME_OVER; + + if(custScenes != NULL) + { + strcpy(tmpBuff, custScenes); + strcat(tmpBuff, "gameover.txt"); + if(testpackfile(tmpBuff, packfile) >= 0) + { + playscene(tmpBuff); + done = 1; + } + } + else + { + if(testpackfile("data/scenes/gameover.txt", packfile) >= 0) + { + playscene("data/scenes/gameover.txt"); + done = 1; + } + + } + + while(!done) + { + font_printf(_strmidx(3, Tr("GAME OVER")), 110 + videomodes.vShift, 3, 0, Tr("GAME OVER")); + done |= (_time > global_config.game_speed * 8 && !sound_query_music(NULL, NULL)); + done |= (bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)); + update(0, 0); + } + + screen_status &= ~IN_SCREEN_GAME_OVER; +} + + + + +void hallfame(int addtoscore) +{ + int done = 0; + int topten[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint64_t score; + char name[MAX_NAME_LEN + 1]; + int i, p, y; + char tmpBuff[MAX_BUFFER_LEN] = {""}; + int col1 = -8; + int col2 = 6; + + screen_status |= IN_SCREEN_HALL_OF_FAME; + + if(hiscorebg) + { + // New alternative background path. + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "hiscore"); + load_background(tmpBuff); + } + else + { + load_cached_background("data/bgs/hiscore"); + } + } + + if(addtoscore) + { + for(p = 0; p < levelsets[current_set].maxplayers; p++) + { + if(player[p].score > savescore.highsc[9]) + { + savescore.highsc[9] = player[p].score; + strcpy(savescore.hscoren[9], player[p].name); + topten[9] = 1; + + for(i = 8; i >= 0 && player[p].score > savescore.highsc[i]; i--) + { + score = savescore.highsc[i]; + strcpy(name, savescore.hscoren[i]); + savescore.highsc[i] = player[p].score; + strcpy(savescore.hscoren[i], player[p].name); + topten[i] = 1; + savescore.highsc[i + 1] = score; + strcpy(savescore.hscoren[i + 1], name); + topten[i + 1] = 0; + } + } + } + saveHighScoreFile(); + } + + _time = 0; + + while(!done) + { + y = 56; + if(!hiscorebg) + { + font_printf(_strmidx(3, Tr("Hall Of Fame")), y - fontheight(3) - 10 + videomodes.vShift, 3, 0, Tr("Hall Of Fame")); + } + + for(i = 0; i < 10; i++) + { + font_printf(_colx(topten[i], col1), y + videomodes.vShift, topten[i], 0, "%2i. %s", i + 1, savescore.hscoren[i]); + font_printf(_colx(topten[i], col2), y + videomodes.vShift, topten[i], 0, (scoreformat ? "%09" PRIu64 : "%" PRIu64), savescore.highsc[i]); + y += (videomodes.vRes - videomodes.vShift - 56 - 32) / 10; //font_heights[topten[i]] + 6; + } + + // Kratus (01-2023) Added the "FLAG_ANYBUTTON" to exit the Hall of Fame screen + update(0, 0); + done |= (_time > global_config.game_speed * 8); + done |= (bothnewkeys & (FLAG_START | FLAG_ANYBUTTON | FLAG_ESC)); + } + unload_background(); + screen_status &= ~IN_SCREEN_HALL_OF_FAME; +} + + + + +// Level completed, show bonus stuff +void showcomplete(int num) +{ + int done = 0; + int i, j, k; + u32 clearbonus[4] = { 10000, 10000, 10000, 10000 }; + u32 lifebonus[4] = { 10000, 10000, 10000, 10000 }; + u32 rushbonus[4] = { 10000, 10000, 10000, 10000 }; + u32 nexttime = 0; + u32 finishtime = 0; + int chan = 0; + char tmpBuff[MAX_BUFFER_LEN] = {""}; + + screen_status |= IN_SCREEN_SHOW_COMPLETE; + + if(completebg) + { + // New alternative background path. + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "complete"); + load_background(tmpBuff); + } + else + { + load_cached_background("data/bgs/complete"); + } + } + + music("data/music/complete", 0, 0); + + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(rush[0] >= 1 && showrushbonus == 1) + { + rushbonus[i] = nomaxrushreset[i] * scbonuses[2]; + } + if(scbonuses[3] == 1) + { + clearbonus[i] = num * scbonuses[0]; + } + else + { + clearbonus[i] = scbonuses[0]; + } + lifebonus[i] = player[i].lives * scbonuses[1]; + } + + update(0, 0); + + _time = 0; + while(!done) + { + if(!scomplete[5]) + { + font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage %i Complete!"), num); + } + else + { + font_printf(videomodes.hShift + scomplete[0], videomodes.vShift + scomplete[1], 3, 0, Tr("Stage")); + font_printf(videomodes.hShift + scomplete[2], videomodes.vShift + scomplete[3], 3, 0, "%i", num); + font_printf(videomodes.hShift + scomplete[4], videomodes.vShift + scomplete[5], 3, 0, Tr("Complete")); + } + + font_printf(videomodes.hShift + cbonus[0], videomodes.vShift + cbonus[1], 0, 0, Tr("Clear Bonus")); + for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0) + { + font_printf(videomodes.hShift + cbonus[j], videomodes.vShift + cbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), clearbonus[i]); + } + font_printf(videomodes.hShift + lbonus[0], videomodes.vShift + lbonus[1], 0, 0, Tr("Life bonus")); + for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0) + { + font_printf(videomodes.hShift + lbonus[j], videomodes.vShift + lbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), lifebonus[i]); + } + if(rush[0] >= 1 && showrushbonus == 1) + { + font_printf(videomodes.hShift + rbonus[0], videomodes.vShift + rbonus[1], 0, 0, Tr("Rush Bonus")); + for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0) + { + font_printf(videomodes.hShift + rbonus[j], videomodes.vShift + rbonus[k], 0, 0, (scoreformat ? "%09lu" : "%lu"), rushbonus[i]); + } + } + font_printf(videomodes.hShift + tscore[0], videomodes.vShift + tscore[1], 0, 0, Tr("Total Score")); + for(i = 0, j = 2, k = 3; i < levelsets[current_set].maxplayers; i++, j = j + 2, k = k + 2) if(player[i].lives > 0) + { + font_printf(videomodes.hShift + tscore[j], videomodes.vShift + tscore[k], 0, 0, (scoreformat ? "%09" PRIu64 : "%" PRIu64), player[i].score); + } + + while(_time > nexttime) + { + if(!finishtime) + { + finishtime = _time + 4 * global_config.game_speed; + } + + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].lives > 0) + { + if(clearbonus[i] > 0) + { + addscore(i, 10); + clearbonus[i] -= 10; + finishtime = 0; + } + else if(lifebonus[i] > 0) + { + addscore(i, 10); + lifebonus[i] -= 10; + finishtime = 0; + } + else if(rush[0] >= 1 && showrushbonus == 1 && (rushbonus[i] > 0)) + { + addscore(i, 10); + rushbonus[i] -= 10; + finishtime = 0; + } + } + } + + if(!finishtime && !(nexttime & 15)) + { + sound_stop_sample(chan); + chan = sound_play_sample(global_sample_list.beep, 0, savedata.effectvol / 2, savedata.effectvol / 2, 100); + } + nexttime++; + } + + if(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC)) + { + done = 1; + } + if(finishtime && _time > finishtime) + { + done = 1; + } + + update(0, 0); + } + + // Add remainder of score, incase player skips counter + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].lives > 0) + { + if(rush[0] >= 1 && showrushbonus == 1) + { + addscore(i, rushbonus[i]); + } + addscore(i, clearbonus[i]); + addscore(i, lifebonus[i]); + } + } + unload_background(); + + screen_status &= ~IN_SCREEN_SHOW_COMPLETE; +} + +void savelevelinfo() +{ + int i; + s_set_entry *set = levelsets + current_set; + s_savelevel *save = savelevel + current_set; + + save->flag = set->saveflag; + // don't check flag here save all info, for simple logic + for(i = 0; i < set->maxplayers; i++) + { + save->pLives[i] = player[i].lives; + save->pCredits[i] = player[i].credits; + save->pScores[i] = player[i].score; + save->pSpawnhealth[i] = player[i].spawnhealth; + save->pSpawnmp[i] = player[i].spawnmp; + save->pWeapnum[i] = player[i].weapnum; + save->pColourmap[i] = player[i].colourmap; + strncpy(save->pName[i], player[i].name, MAX_NAME_LEN); + } + save->credits = credits; + save->level = current_level; + save->stage = current_stage; + save->which_set = current_set; + strncpy(save->dName, set->name, MAX_NAME_LEN - 1); + set_saved_allowselect_arguments(current_set, allowselect_args); +} + +void tryvictorypose(entity *ent) +{ + if( ent && + ent->inpain & ~IN_PAIN_NONE && + !ent->falling && + !(ent->death_state & DEATH_STATE_DEAD) && + !ent->rising && + (ent->idling & IDLING_ACTIVE) && + ent->position.y <= ent->base ) + { + ent->takeaction = NULL; + ent->velocity.x = ent->velocity.z = 0; + ent->idling = IDLING_NONE; + ent_set_anim(ent, ANI_VICTORY, 0); + } +} + +static void check_victory_pose() +{ + if ( (endgame & 1) && level_completed_defeating_boss ) + { + int i; + for(i = 0; i < MAX_PLAYERS; i++) + { + entity* tmp = self; + self = player[i].ent; + if(self && validanim(self, ANI_VICTORY) && self->animnum != ANI_VICTORY) + { + tryvictorypose(self); + endgame = 0; + } + else if (self && self->animnum == ANI_VICTORY && self->animating) + { + endgame = 0; + } + + self = tmp; + } + } +} + +int playlevel(char *filename) +{ + int i, type, p_alive = 0; + + kill_all(); + + savelevelinfo(); // just in case we lose them after level is freed + + load_level(filename); + + if(!nosave) + { + saveGameFile(); + saveHighScoreFile(); + saveScriptFile(); + } + nosave = 0; + + _time = 0; + nextplan = 0; + stalker = NULL; + firstplayer = NULL; + type = level->type; + + // Fixes the start level executing last button bug + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].lives > 0) + { + player[i].disablekeys = player[i].newkeys = player[i].playkeys = 0; + player[i].weapnum = level->setweap; + player[i].joining = 0; + player[i].hasplayed = 1; + spawnplayer(i); + player[i].ent->rush.max = 0; + } + } + + //execute a script when level started + if(Script_IsInitialized(&level_script)) + { + Script_Execute(&level_script); + } + if(Script_IsInitialized(&(level->level_script))) + { + Script_Execute(&(level->level_script)); + } + + while(!endgame) + { + update(1, 0); + + if (level->force_finishlevel) + { + level_completed = 1; + endgame |= 1; + level->force_finishlevel = 0; + } + if (level->force_gameover) + { + //entity* temp = self; + for(i = 0; i < MAX_PLAYERS; i++) //levelsets[current_set].maxplayers + { + player[i].lives = 0; + if(noshare) + { + player[i].credits = 0; + } + else + { + credits = 0; + } + if (player[i].ent) + { + kill_entity(player[i].ent, KILL_ENTITY_TRIGGER_LEVEL_GAME_OVER); + player[i].ent = NULL; + } + //self = player[i].ent; + //player_die(); + } + //self = temp; + //kill_all(); + endgame |= 1; + level->force_gameover = 0; + } + if(level_completed) + { + endgame |= (!findent(TYPE_ENEMY) || level->type || findent(TYPE_ENDLEVEL)); // Ends when all enemies die or a bonus level + check_victory_pose(); + } + } + //execute a script when level finished + if(Script_IsInitialized(&endlevel_script)) + { + Script_Execute(&endlevel_script); + } + if(Script_IsInitialized(&(level->endlevel_script))) + { + Script_Execute(&(level->endlevel_script)); + } + if(!nofadeout) + { + fade_out(0, 0); + } + + for(i = 0; i < levelsets[current_set].maxplayers; i++) + { + if(player[i].ent) + { + nomaxrushreset[i] = player[i].ent->rush.max; + player[i].spawnhealth = player[i].ent->energy_state.health_current; + player[i].spawnmp = player[i].ent->energy_state.mp_current; + } + // reset + player[i].weapnum = 0; + } + + if(!musicoverlap) + { + sound_close_music(); + } + sound_stopall_sample(false); + + unload_level(); + + // Are any players alive? + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].lives > 0) + { + p_alive = 1; + break; + } + } + + return ((type == 2 && endgame != 2) || p_alive); +} + +// Caskey, Damon V (retool, OA unknown) +// 2019-01-03 +// +// For select screen. Spawn sample entity for player_index. +static entity *spawnexample(int player_index) +{ + char* SPAWN_MODEL_NAME = NULL; + const int SPAWN_MODEL_INDEX = MODEL_INDEX_NONE; + + entity *example; + s_model *model; + s_set_entry *set; + + float pos_x; + float pos_y; + float pos_z; + int direction; + + set = levelsets + current_set; + + // Get spawn attributes and spawn entity. + pos_x = (float)psmenu[player_index][0]; + pos_y = 0; + pos_z = (float)psmenu[player_index][1]; + direction = spdirection[player_index]; + + // Next selectable model in cycle. We'll use this + // to decide what we spawn. + model = nextplayermodeln(NULL, player_index); + + example = spawn(pos_x, pos_z, pos_y, direction, SPAWN_MODEL_NAME, SPAWN_MODEL_INDEX, model); + + // Copy model's name to player property. + strcpy(player[player_index].name, model->name); + + // If color selection is allowed and we want + // players with same models to use different + // maps, then use next map in cycle. Otherwise + // just go with default map. + if (colourselect && (set->nosame & 2)) + { + player[player_index].colourmap = nextcolourmapn(model, -1, player_index); + } + else + { + player[player_index].colourmap = 0; + } + + // Apply map to spawned entity. + ent_set_colourmap(example, player[player_index].colourmap); + + // So the entity knows how it came to be. + example->spawntype = SPAWN_TYPE_PLAYER_SELECT; + + return example; +} + +// load saved select screen +static void load_select_screen_info(s_savelevel *save) +{ + int i = 0; + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + char *filename = ""; // not used, just fused into GET_INT_ARG() + char *command = ""; // not used, just fused into GET_INT_ARG() + + // load model_loads + for(i = 0; i < save->selectLoadCount; i++) + { + s_model *tempmodel; + command = GET_ARG(0); + + if(!command || !command[0]) continue; + ParseArgs(&arglist, save->selectLoad[i], argbuf); + + tempmodel = findmodel(GET_ARG(1)); + if (tempmodel) + { + update_model_loadflag(tempmodel, GET_INT_ARG(2)); + } + } + + ParseArgs(&arglist, save->selectMusic, argbuf); + command = GET_ARG(0); + if(command && command[0]) music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3))); + + ParseArgs(&arglist, save->selectBackground, argbuf); + command = GET_ARG(0); + if(command && command[0]) load_background(GET_ARG(1)); + + return; +} + +int selectplayer(int *players, char *filename, int useSavedGame) +{ + s_model *tempmodel; + s_model *model_new[MAX_PLAYERS] = { NULL }; + int i; + int exit = 0; + int escape = 0; + int defaultselect = 0; + unsigned exitdelay = 0; + int players_busy = 0; + int players_ready = 0; + char string[MAX_BUFFER_LEN] = { "" }; + char *buf, *command; + size_t size = 0; + ptrdiff_t pos = 0; + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + s_set_entry *set = levelsets + current_set; + s_savelevel *save = savelevel + current_set; + int load_count = 0; + int saved_select_screen = 0; + int is_first_select = 1; + + savelevelinfo(); + + screen_status |= IN_SCREEN_SELECT; + + kill_all(); + + // Initialize player sized arrays. + entity *example[set->maxplayers]; + int ready[set->maxplayers]; + + // Initialize + for (i = 0; i < set->maxplayers; i++) + { + example[i] = NULL; + ready[i] = 0; + } + + // Allow select? 'a' is the first char of allowselect, + // if there's 'a' then there is allowselect. + if (!allowselect_args + || (allowselect_args[0] != 'a' + && allowselect_args[0] != 'A')) + { + reset_playable_list(1); + } + + // Reset memory for player array. + memset(player, 0, sizeof(*player) * MAX_PLAYERS); + + // Load game selected and a save game available? + if (useSavedGame && save) + { + if (save->selectFlag) + { + load_select_screen_info(save); + load_playable_list( + get_saved_allowselect_arguments(current_set) + ); + saved_select_screen = 1; + } + } + + // Mark "hasplayed" for all players. + for (i = 0; i < set->maxplayers; i++) + { + player[i].hasplayed = players[i]; + } + + for (i = 0; i < set->maxplayers; i++) + { + if (savelevel[current_set].pLives[i] > 0) + { + is_first_select = 0; + break; + } + } + + // Load and apply selection text file. + if (filename && filename[0]) + { + if (buffer_pakfile(filename, &buf, &size) != 1) + { + borShutdown(1, "Failed to load player select file '%s'", filename); + } + while (pos < size) + { + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + if (command && command[0]) + { + if (stricmp(command, "music") == 0) + { + music(GET_ARG(1), GET_INT_ARG(2), atol(GET_ARG(3))); + // SAVE + multistrcatsp(save->selectMusic, command, GET_ARG(1), GET_ARG(2), GET_ARG(3), NULL); + } + else if (stricmp(command, "allowselect") == 0) + { + load_playable_list(buf + pos); + set_saved_allowselect_arguments( + current_set, + allowselect_args + ); + } + else if (stricmp(command, "background") == 0) + { + load_background(GET_ARG(1)); + // SAVE + multistrcatsp(save->selectBackground, command, GET_ARG(1), NULL); + } + else if (stricmp(command, "load") == 0) + { + tempmodel = findmodel(GET_ARG(1)); + if (!tempmodel) + { + load_cached_model(GET_ARG(1), filename, GET_INT_ARG(2)); + } + else + { + update_model_loadflag(tempmodel, GET_INT_ARG(2)); + } + // SAVE + if (load_count < MAX_SELECT_LOADS) + { + multistrcatsp(save->selectLoad[load_count], command, GET_ARG(1), GET_ARG(2), NULL); + load_count++; + } + } + else if (command && command[0]) + { + printf("Command '%s' is not understood in file '%s'\n", command, filename); + } + } + + pos += getNewLineStart(buf + pos); + } + save->selectLoadCount = load_count; // SAVE number of LOAD command + save->selectFlag = 1; + + if (buf != NULL) + { + free(buf); + buf = NULL; + } + } + else // without select.txt + { + if (is_first_select || (!skipselect[0][0] && !set->noselect)) // no select is skipselect without names + { + defaultselect = 1; // normal select or skipselect/noselect? 1 == normal select + } + + if (!noshare) + { + credits = CONTINUES; + } + else for (i = 0; i < set->maxplayers; i++) + { + if (players[i]) + { + player[i].credits = CONTINUES; + } + } + + if (skipselect[0][0] || set->noselect) + { + for (i = 0; i < set->maxplayers; i++) + { + if (!players[i]) + { + continue; + } + strncpy(player[i].name, skipselect[i], MAX_NAME_LEN); + + if (defaultselect) + { + player[i].lives = PLAYER_LIVES; + if (!(global_config.cheats & CHEAT_OPTIONS_CREDITS_ACTIVE)) + { + if (noshare) + { + --player[i].credits; + } + else + { + --credits; + } + } + } + else + { + player[i].lives = savelevel[current_set].pLives[i]; + player[i].score = savelevel[current_set].pScores[i]; + if (noshare) player[i].credits = savelevel[current_set].pCredits[i]; + else credits = savelevel[current_set].credits; + } + } + + screen_status &= ~IN_SCREEN_SELECT; + + return 1; + } + + if (!saved_select_screen) + { + if (unlockbg && bonus) + { + // New alternative background path. + if (custBkgrds != NULL) + { + strcpy(string, custBkgrds); + strcat(string, "unlockbg"); + load_background(string); + } + else + { + load_cached_background("data/bgs/unlockbg"); + } + } + else + { + // New alternative background path. + if (custBkgrds != NULL) + { + strcpy(string, custBkgrds); + strcat(string, "select"); + load_background(string); + } + else + { + load_cached_background("data/bgs/select"); + } + } + if (!music("data/music/menu", 1, 0)) + { + music("data/music/remix", 1, 0); + } + } + } + + for (i = 0; i < set->maxplayers; i++) + { + if (players[i]) + { + example[i] = spawnexample(i); + player[i].playkeys = 0; + if (defaultselect) + { + player[i].lives = PLAYER_LIVES; + if (!(global_config.cheats & CHEAT_OPTIONS_CREDITS_ACTIVE)) + { + if (noshare) + { + --player[i].credits; + } + else + { + --credits; + } + } + } + else + { + player[i].lives = savelevel[current_set].pLives[i]; + player[i].score = savelevel[current_set].pScores[i]; + if (noshare) player[i].credits = savelevel[current_set].pCredits[i]; + else credits = savelevel[current_set].credits; + } + } + } + + _time = 0; + + // Stay in selection until escape or exit. + // + // exit = all players ready (selected) and exit delay expired. + // escape = Escape key pressed. + while (!(exit || escape)) + { + players_busy = 0; + players_ready = 0; + + // Loop through players. + for (i = 0; i < set->maxplayers; i++) + { + // Current player index not yet selected? + if (!ready[i]) + { + + // This is where we present player selections. The logic is long + // and a little messy, so buckle up! Basically, we want to spawn + // an example entity to get started, and that example entity + // is what player sees on the selection screen. Then we switch + // its model/color/animation based on the situation and player + // input. + // + // 1. If an example entity exists and is playing a transition + // then we... + // + // a) do nothing if the animation isn't finished. + // + // b) If it IS finished... + // + // -- 1. If the animation is ANI_SELECTIN, then play ANI_SELECT. + // + // -- 2. If aniamton is ANI_SELECTOUT, then we switch to new + // model (if available). + // + // 2. If player hasn't played yet, has some credits or + // can draw from credit pool and pressed any action button, + // then we'll deal with their credit pool and spawn the + // first example (selectable model preview). Having an + // example spawned also tells us the player has completed + // this step, and so it's OK to run actions from any of + // the others. + // + // 3. If the player pressed Left or Right instead and there's + // an example spawned, then we find the previous/next + // character in line, and record it to a variable. Then we + // see if example has ANI_SELECTIN. if it doesn't we switch + // to new model. If it does, play ANI_SELECT. + // + // 4. If the player presses Up or Down, we have an example + // spawned and colourselect is enabled, then cycle to the + // model's previous/next color set choice. + // + // 5. If the player presses any action button and we have + // an example spawned, then we mark the player's ready delay + // flag, and stalltime. See the parent logic block for + // selection delay & exit details. This is the player + // making their selection choice. + + // Example exists and select transition animation? + if (example[i] + && (example[i]->animnum == ANI_SELECTIN || example[i]->animnum == ANI_SELECTOUT)) + { + // If still animating than do nothing. Let the transition finish. + if (example[i]->animating) + { + } + else + { + // Transition to select animation. + if (example[i]->animnum == ANI_SELECTIN) + { + ent_set_anim(example[i], ANI_SELECT, 0); + } + + // Transition from select (player selected another model, and the + // select out transition is now finished). Repeat of left/right key + // logic below and probably needs consolidation. + if (example[i]->animnum == ANI_SELECTOUT && model_new[i]) + { + // Apply new model. + ent_set_model(example[i], model_new[i]->name, 0); + model_new[i] = NULL; + + // Copy example model name to player name variable. + strcpy(player[i].name, example[i]->model->name); + + // If colorselect is enabled and nosame 2 is enabled, skip to + // start at next avaialble color cycle. Otherwise just start + // with default color set (0). + if (colourselect && (set->nosame & 2)) + { + player[i].colourmap = nextcolourmapn(example[i]->model, -1, i); + } + else + { + player[i].colourmap = 0; + } + + // Apply color set. + ent_set_colourmap(example[i], player[i].colourmap); + } + } + } + else if (!player[i].hasplayed + && (noshare || credits > 0) + && (player[i].newkeys & FLAG_ANYBUTTON)) + { + + // Now this player has played. + players[i] = player[i].hasplayed = 1; + //printf("%d %d %d\n", i, player[i].lives, immediate[i]); + + // Noshare means each player has their own credit pool. + if (noshare) + { + player[i].credits = CONTINUES; + } + + // Credits cheat = infinite credits. If that's not enabled, + // then deduct a credit. + if (!(global_config.cheats & CHEAT_OPTIONS_CREDITS_ACTIVE)) + { + if (noshare) + { + --player[i].credits; + } + else + { + --credits; + } + } + + // Give player default number of lives, spawn + // example model and cancel the key flag. + player[i].lives = PLAYER_LIVES; + example[i] = spawnexample(i); + player[i].playkeys = 0; + + // Play sound effect. + if (global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + else if ((player[i].newkeys & FLAG_ANYBUTTON) && example[i]) //Kratus (01-05-21) Moved the "anybutton" code to before of the "left/right" code to fix a bug that makes no character chosen when both are pressed together + { + if (global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + // yay you picked me! + if (validanim(example[i], ANI_PICK)) + { + ent_set_anim(example[i], ANI_PICK, 0); + } + example[i]->stalltime = _time + global_config.game_speed * 2; + ready[i] = 1; + } + else if (player[i].newkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT) && example[i]) + { + // Give player a feedback sound. + if (global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + // Get model in use right now. + // Let's get the new model. Left key = previous model + // in cycle. Right key = next. + if ((player[i].newkeys & FLAG_MOVELEFT)) + { + model_new[i] = prevplayermodeln(example[i]->model, i); + } + else + { + model_new[i] = nextplayermodeln(example[i]->model, i); + } + + // Do we have a select out transition? If so play it here. + // Otherwise switch to new model. + if (validanim(example[i], ANI_SELECTOUT)) + { + ent_set_anim(example[i], ANI_SELECTOUT, 0); + } + else + { + // Apply new model. + ent_set_model(example[i], model_new[i]->name, 0); + model_new[i] = NULL; + + // Copy example model name to player name variable. + strcpy(player[i].name, example[i]->model->name); + + // If colorselect is enabled and nosame 2 is enabled, skip to + // start at next avaialble color cycle. Otherwise just start + // with default color set (0). + if (colourselect && (set->nosame & 2)) + { + player[i].colourmap = nextcolourmapn(example[i]->model, -1, i); + } + else + { + player[i].colourmap = 0; + } + + // Apply color set. + ent_set_colourmap(example[i], player[i].colourmap); + } + } + else if (player[i].newkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN) && colourselect && example[i]) + { + player[i].colourmap = ((player[i].newkeys & FLAG_MOVEUP) ? nextcolourmapn : prevcolourmapn)(example[i]->model, player[i].colourmap, i); + ent_set_colourmap(example[i], player[i].colourmap); + } + } + else if (ready[i] == 1) + { + if (((!validanim(example[i], ANI_PICK) || example[i]->modeldata.animation[ANI_PICK]->loop.mode) && _time > example[i]->stalltime) || !example[i]->animating) + { + ready[i] = 2; + exitdelay = _time + global_config.game_speed; + } + } + else if (ready[i] == 2) + { + font_printf(psmenu[i][2], psmenu[i][3], 0, 0, Tr("Ready!")); + } + + if (example[i] != NULL) + { + players_busy++; + } + if (ready[i] == 2) + { + players_ready++; + } + } + + if (players_busy && players_busy == players_ready && exitdelay && _time > exitdelay) + { + exit = 1; + } + update(0, 0); + + if (bothnewkeys & FLAG_ESC || escape_flag == 11) //Kratus (20-04-21) Added the new "escape" flag in the select screen by using the "gotomainmenu" function and the flag "11" + { + escape = 1; + escape_flag = 0; + } + } + + // No longer at the select screen + kill_all(); + sound_close_music(); + screen_status &= ~IN_SCREEN_SELECT; + + return (!escape); +} + +void playgame(int *players, unsigned which_set, int useSavedGame) +{ + int i; + current_level = 0; + current_stage = 1; + current_set = which_set; + s_set_entry *set = levelsets + current_set; + s_savelevel *save = savelevel + current_set; + s_level_entry *le; + + useSave = 0; + useSet = -1; + + if(which_set >= num_difficulties) + { + return; + } + // borShutdown(1, "Illegal set chosen: index %i (there are only %i sets)!", which_set, num_difficulties); + + allow_secret_chars = set->ifcomplete; + PLAYER_LIVES = set->lives; + musicoverlap = set->musicoverlap; + fade = set->custfade; + CONTINUES = set->credits; + magic_type = set->typemp; + if(PLAYER_LIVES == 0) + { + PLAYER_LIVES = 3; // default + } + if(CONTINUES == 0) + { + CONTINUES = 5; // default + } + if(fade == 0) + { + fade = 24; // default + } + sameplayer = set->nosame; + + if(useSavedGame == 1 && save->flag) + { + memset(player, 0, sizeof(*player) * 4); + if(!loadScriptFile()) + { + printf("Warning, failed to load script save!\n"); + } + current_level = save->level; + current_stage = save->stage; + if(save->flag == 2) // don't check 1 or 0 becuase if we use saved game the flag must be >0 + { + for(i = 0; i < set->maxplayers; i++) + { + player[i].lives = save->pLives[i]; + player[i].credits = save->pCredits[i]; + player[i].score = save->pScores[i]; + player[i].colourmap = save->pColourmap[i]; + player[i].weapnum = save->pWeapnum[i]; + player[i].spawnhealth = save->pSpawnhealth[i]; + player[i].spawnmp = save->pSpawnmp[i]; + strncpy(player[i].name, save->pName[i], MAX_NAME_LEN); + } + credits = save->credits; + } + load_playable_list( + get_saved_allowselect_arguments(current_set) + ); + //reset_playable_list(1); // add this because there's no select screen, temporary solution + } + + nosave = 1; + + // fix save an inexistent level + if ( current_level >= set->numlevels ) + { + current_level = set->numlevels-1; + } + + le = set->levelorder + current_level; + set->noselect = le->noselect; + if (save->selectSkipSelect[0]) le = load_skipselect(save->selectSkipSelect, set); // LOAD skipselect + for(i = 0; i < MAX_PLAYERS; i++) + { + if(le->skipselect[i]) + { + strcpy(skipselect[i], le->skipselect[i]); + } + else + { + skipselect[i][0] = 0; + } + } + + if((useSavedGame == 1 && save->flag == 2) || useSavedGame == 2 || selectplayer(players, NULL, useSavedGame)) // if save flag is 2 don't select player + { + while(current_level < set->numlevels) + { + if(branch_name[0]) // branch checking + { + //current_stage = 1; //jump, jump... perhaps we don't need to reset it, modders should take care of it. + for(i = 0; i < set->numlevels; i++) + { + if(set->levelorder[i].branchname && stricmp(set->levelorder[i].branchname, branch_name) == 0) + { + current_level = i; + break; + } + //if(levelorder[which_set][i]->gonext==1) ++current_stage; OX. Commented this line out. Seems to be cause of inacurate stage # complete message. + } + branch_name[0] = 0;// clear up so we won't stuck here + } + le = set->levelorder + current_level; + PLAYER_MIN_Z = le->z_coords[0]; + PLAYER_MAX_Z = le->z_coords[1]; + BGHEIGHT = le->z_coords[2]; + + if(le->type == LE_TYPE_CUT_SCENE) + { + playscene(le->filename); + } + else if(le->type == LE_TYPE_SELECT_SCREEN) + { + memset(save->selectSkipSelect,0,sizeof(save->selectSkipSelect)); // RESET skipselect + set->noselect = 0; + for(i = 0; i < set->maxplayers; i++) // reset skipselect + { + if(le->skipselect[i]) + { + skipselect[i][0] = 0; + } + } + for(i = 0; i < set->maxplayers ; i++) + { + players[i] = (player[i].lives > 0); + } + if(selectplayer(players, le->filename, useSavedGame) == 0) + { + break; + } + } + else if(le->type == LE_TYPE_SKIP_SELECT) + { + set->noselect = le->noselect; + for(i = 0; i < MAX_PLAYERS; i++) + { + if(le->skipselect[i]) + { + strcpy(skipselect[i], le->skipselect[i]); + } + else + { + skipselect[i][0] = 0; + } + } + save_skipselect(save->selectSkipSelect, skipselect); // TO SAVE FILE + selectplayer(players, NULL, useSavedGame); // re-select a player + } + else if(!playlevel(le->filename)) + { + int all_p_lives_zero = 0; + for(i = 0; i < MAX_PLAYERS; i++) + { + if (player[i].lives <= 0) ++all_p_lives_zero; + } + all_p_lives_zero = (all_p_lives_zero >= MAX_PLAYERS) ? 1 : 0; + + //if( (player[0].lives <= 0 && player[1].lives <= 0 && player[2].lives <= 0 && player[3].lives <= 0) ) + if(all_p_lives_zero) + { + if( (!set->noshowgameover && !(goto_mainmenu_flag&2)) ) + { + gameover(); + } + if(!set->noshowhof && !(goto_mainmenu_flag&4)) + { + hallfame(1); + } + for(i = 0; i < set->maxplayers; i++) + { + player[i].hasplayed = 0; + player[i].weapnum = 0; + } + } + break; + } + if(le->gonext == 1) + { + if (!set->noshowcomplete) showcomplete(current_stage); + for(i = 0; i < set->maxplayers; i++) + { + player[i].spawnhealth = 0; + player[i].spawnmp = 0; + } + ++current_stage; + save->stage = current_stage; + } + current_level++; + le = set->levelorder + current_level; + save->level = current_level; + //2007-2-24, gonext = 2, end game + if((le - 1)->gonext == 2) + { + current_level = set->numlevels; + } + if(useSave) + { + goto endgame; //quick exit without saving, for script load game logic + } + }//while + + if(current_level >= set->numlevels) + { + /* + Kratus (01-2024) Reset level/stage values when a game is finished before saving it. + It will automatically erase the current game progress but only after the ending. + However, it will maintain the "times_completed" intact. + Without this fix the player can increase the "times_completed" infinitely by + just finishing the game once and loading the ending directly how many times he want. + */ + bonus += save->times_completed++; + current_level = 0; + current_stage = 1; + save->level = current_level; + save->stage = current_stage; + saveGameFile(); + if(!nofadeout) + { + fade_out(0, 0); + } + if(!set->noshowhof) + { + hallfame(1); + } + } + } + +endgame: + // clear global script variant list + branch_name[0] = 0; + sound_close_music(); +} + +int menu_difficulty() +{ + int quit = 0; + int selector = 0; + int maxdisplay = 5; + int i, j, t; + //float slider = 0; + int barx, bary, barw, barh; + s_drawmethod drawmethod = plainmethod; + drawmethod.alpha = BLEND_MODE_ALPHA; + + barx = videomodes.hRes / 5; + bary = _liney(0, 0) - 2; + barw = videomodes.hRes * 3 / 5; + barh = 5 * (fontheight(0) + 1) + 4; + screen_status |= IN_SCREEN_NEW_GAME_MENU; + bothnewkeys = 0; + + loadGameFile(); + + while(!quit) + { + if(num_difficulties > 1) + { + _menutextm(2, -2, 0, Tr("Game Mode")); + t = (selector - (selector == num_difficulties)) / maxdisplay * maxdisplay; + for(j = 0, i = t; i < maxdisplay + t && i < num_difficulties; j++, i++) + { + if(j < maxdisplay) + { + // Kratus (10-2021) Added translation feature to the "set" name + // It will get the "set" name in the "levels.txt" file and translate it + // You need to add a new "msgid/msgstr" instance in your "translation.txt" file and put + // the same names as used by all level sets + if(bonus >= levelsets[i].ifcomplete) + { + _menutextm((selector == i), j, 0, "%s", Tr(levelsets[i].name)); + } + else + { + if(levelsets[i].ifcomplete > 1) + { + _menutextm((selector == i), j, 0, Tr("%s - Finish Game %i Times To UnLock"), Tr(levelsets[i].name), levelsets[i].ifcomplete); + } + else + { + _menutextm((selector == i), j, 0, Tr("%s - Finish Game To UnLock"), Tr(levelsets[i].name)); + } + } + } + else + { + break; + } + } + _menutextm((selector == i), 6, 0, Tr("Back")); + + //draw the scroll bar + if(num_difficulties > maxdisplay) + { + spriteq_add_box(barx, bary, barw, barh, 0, color_black, &drawmethod); //outerbox + spriteq_add_line(barx, bary, barx + 8, bary, 1, color_white, NULL); + spriteq_add_line(barx, bary, barx, bary + barh, 1, color_white, NULL); + spriteq_add_line(barx + 8, bary, barx + 8, bary + barh, 1, color_white, NULL); + spriteq_add_line(barx, bary + barh, barx + 8, bary + barh, 1, color_white, NULL); + spriteq_add_box(barx + 1, bary + selector * (barh - 3) / num_difficulties, 7, 3, 2, color_white, NULL); //slider + } + } + + update(0, 0); + + if(num_difficulties == 1) // OX. Mods with only one set will auto load that difficulty. + { + if(selector == num_difficulties) + { + quit = 1; + } + else if(bonus >= levelsets[selector].ifcomplete) + { + saveslot = selector; + strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN - 1); + screen_status &= ~IN_SCREEN_NEW_GAME_MENU; + return saveslot; + } + } + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(selector < 0) + { + selector = num_difficulties; + } + if(selector > num_difficulties) + { + selector = 0; + } + //if(selector= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(selector == num_difficulties) + { + quit = 1; + } + else if(bonus >= levelsets[selector].ifcomplete) + { + saveslot = selector; + strncpy(savelevel[saveslot].dName, levelsets[saveslot].name, MAX_NAME_LEN - 1); + screen_status &= ~IN_SCREEN_NEW_GAME_MENU; + return saveslot; + } + } + } + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_NEW_GAME_MENU; + return -1; +} + +int load_saved_game() +{ + int quit = 0; + int selector = 0; + int savedStatus = 0; + char name[MAX_BUFFER_LEN] = {""}; + int col1 = -8, col2 = 6; + + screen_status |= IN_SCREEN_LOAD_GAME_MENU; + bothnewkeys = 0; + + if((savedStatus = loadGameFile())) + { + getPakName(name, 0); + } + for(saveslot = 0; saveslot < num_difficulties; saveslot++) if(savelevel[saveslot].flag && savelevel[saveslot].level) + { + break; + } + + while(!quit) + { + if(saveslot >= num_difficulties) // not found + { + _menutextm(2, -4, 0, Tr("Load Game")); + _menutext(0, col1, -2, Tr("Saved File:")); + _menutext(0, col2, -2, Tr("Not Found!")); + _menutextm(1, 6, 0, Tr("Back")); + + /* + Kratus (01-2024) Minor fix in the selector number if no save is found. + There's no selector "2" in this menu, only 0 (load) or 1 (back). + */ + selector = 1; + } + else + { + _menutextm(2, -4, 0, Tr("Load Game")); + _menutext(0, col1, -2, Tr("Saved File:")); + if(savedStatus) + { + _menutext(0, col2, -2, "%s", name); + } + else + { + _menutext(0, col2, -2, Tr("Not Found!")); + } + + if(savedStatus) + { + _menutext((selector == 0), col1, -1, Tr("Mode:")); + _menutext((selector == 0), col2, -1, "%s", savelevel[saveslot].dName); + _menutext(0, col1, 0, Tr("Stage:")); + _menutext(0, col2, 0, "%d", savelevel[saveslot].stage); + _menutext(0, col1, 1, Tr("Level:")); + _menutext(0, col2, 1, "%d", savelevel[saveslot].level); + _menutext(0, col1, 2, Tr("Credits:")); + + if(noshare){ + _menutext(0, col2, 2, "%d/%d/%d/%d", + savelevel[saveslot].pCredits[0], + savelevel[saveslot].pCredits[1], savelevel[saveslot].pCredits[2], + savelevel[saveslot].pCredits[3]); + } else { + _menutext(0, col2, 2, "%d", savelevel[saveslot].credits); + } + + _menutext(0, col1, 3, Tr("Player Lives:")); + _menutext(0, col2, 3, "%d/%d/%d/%d", + savelevel[saveslot].pLives[0], + savelevel[saveslot].pLives[1], savelevel[saveslot].pLives[2], + savelevel[saveslot].pLives[3]); + } + _menutextm((selector == 1), 6, 0, Tr("Back")); + } + update(0, 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(selector == 0 && (bothnewkeys & FLAG_MOVELEFT)) + { + while(1) + { + --saveslot; + if(saveslot < 0) + { + saveslot = num_difficulties - 1; + } + if(savelevel[saveslot].flag && savelevel[saveslot].level) + { + break; + } + } + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(selector == 0 && (bothnewkeys & FLAG_MOVERIGHT)) + { + while(1) + { + ++saveslot; + if(saveslot > num_difficulties - 1) + { + saveslot = 0; + } + if(savelevel[saveslot].flag && savelevel[saveslot].level) + { + break; + } + } + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(savedStatus) + { + if(selector < 0) + { + selector = 1; + } + if(selector > 1) + { + selector = 0; + } + } + else + { + selector = 1; + } + + if((bothnewkeys & FLAG_ANYBUTTON)) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + switch(selector) + { + case 0: + return saveslot; + break; + case 1: + quit = 1; + break; + } + } + } + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_LOAD_GAME_MENU; + return -1; +} + +int choose_mode(int *players) +{ + int quit = 0; + int relback = 0; + int selector = 0; + int status = 0; + + screen_status |= IN_SCREEN_GAME_START_MENU; + bothnewkeys = 0; + + while(!quit) + { + _menutextm(2, 1, 0, Tr("Choose Mode")); + _menutextm((selector == 0), 3, 0, Tr("New Game")); + _menutextm((selector == 1), 4, 0, Tr("Load Game")); + _menutextm((selector == 2), 6, 0, Tr("Back")); + + update(0, 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(selector < 0) + { + selector = 2; + } + if(selector > 2) + { + selector = 0; + } + + if(bothnewkeys & FLAG_ANYBUTTON) + { + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + switch(selector) + { + case 0: + status = menu_difficulty(); + if(status != -1) + { + playgame(players, status, 0); + relback = 1; + quit = 1; + } + break; + case 1: + status = load_saved_game(); + if(status != -1) + { + playgame(players, status, 1); + relback = 1; + quit = 1; + } + break; + default: + quit = 1; + break; + } + } + } + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_GAME_START_MENU; + return relback; +} + +void term_videomodes() +{ + videomodes.hRes = 0; + videomodes.vRes = 0; + video_set_mode(videomodes); + if(custScenes != NULL) + { + free(custScenes); + } + custScenes = NULL; + if(custBkgrds != NULL) + { + free(custBkgrds); + } + custBkgrds = NULL; + if(custLevels != NULL) + { + free(custLevels); + } + custLevels = NULL; + if(custModels != NULL) + { + free(custModels); + } + custModels = NULL; +} + +// Load Video Mode from file +void init_videomodes(int log) +{ + char *filename = "data/video.txt"; + int tmp; + ptrdiff_t pos, len; + size_t size; + char *buf = NULL; + char *command = NULL; + char *value = NULL, *value2; + ArgList arglist; + char argbuf[MAX_ARG_LEN + 1] = ""; + + // + + if(log) + { + printf("Initializing video............\n"); + } + + // Use an alternative video.txt if there is one. Some of these are long filenames; create your PAKs with borpak and you'll be fine. +#define tryfile(X) if((tmp=openpackfile(X,packfile))!=-1) { closepackfile(tmp); filename=X; goto readfile; } +#if WIN || LINUX + tryfile("data/videopc.txt"); +#endif +#undef tryfile + +readfile: + // Read file + if(buffer_pakfile(filename, &buf, &size) != 1) + { + videoMode = 0; + printf("'%s' not found.\n", filename); + goto VIDEOMODES; + } + + printf("Reading video settings from '%s'.\n", filename); + + // Now interpret the contents of buf line by line + pos = 0; + while(pos < size) + { + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + + if(command && command[0]) + { + if(stricmp(command, "video") == 0) + { + value = GET_ARG(1); + if((value2 = strchr(value, 'x'))) + { + videomodes.hRes = atoi(value); + videomodes.vRes = atoi(value2 + 1); + videoMode = 255; + } + else + { + videoMode = GET_INT_ARG(1); + } + } + else if(stricmp(command, "scenes") == 0) + { + len = strlen(GET_ARG(1)); + custScenes = malloc(len + 1); + strcpy(custScenes, GET_ARG(1)); + custScenes[len] = 0; + } + else if(stricmp(command, "backgrounds") == 0) + { + len = strlen(GET_ARG(1)); + custBkgrds = malloc(len + 1); + strcpy(custBkgrds, GET_ARG(1)); + custBkgrds[len] = 0; + } + else if(stricmp(command, "levels") == 0) + { + len = strlen(GET_ARG(1)); + custLevels = malloc(len + 1); + strcpy(custLevels, GET_ARG(1)); + custLevels[len] = 0; + } + else if(stricmp(command, "models") == 0) + { + len = strlen(GET_ARG(1)); + custModels = malloc(len + 1); + strcpy(custModels, GET_ARG(1)); + custModels[len] = 0; + } + else if(stricmp(command, "colourdepth") == 0) + { + printf("\nColordepth is not supported anymore. All modules are displayed with a 32bit color screen.\n\n"); + } + else if(stricmp(command, "forcemode") == 0) {} + else if(command && command[0]) + { + printf("Command '%s' not understood in file '%s'!\n", command, filename); + } + } + // Go to next line + pos += getNewLineStart(buf + pos); + } + + if(buf != NULL) + { + free(buf); + buf = NULL; + } + +VIDEOMODES: + videomodes.mode = videoMode; + videomodes.filter = savedata.swfilter; + switch (videoMode) + { + // 320x240 - QVGA + case 0: + videomodes.hRes = 320; + videomodes.vRes = 240; + videomodes.hScale = 1; + videomodes.vScale = 1; + videomodes.hShift = 0; + videomodes.vShift = 0; + videomodes.dOffset = 231; + PLAYER_MIN_Z = 160; + PLAYER_MAX_Z = 232; + BGHEIGHT = 160; + break; + + // 480x272 - WQVGA + case 1: + videomodes.hRes = 480; + videomodes.vRes = 272; + videomodes.hScale = (float)1.5; + videomodes.vScale = (float)1.13; + videomodes.hShift = 80; + videomodes.vShift = 20; + videomodes.dOffset = 263; + PLAYER_MIN_Z = 182; + PLAYER_MAX_Z = 264; + BGHEIGHT = 182; + break; + + // 640x480 - VGA + case 2: + videomodes.hRes = 640; + videomodes.vRes = 480; + videomodes.hScale = 2; + videomodes.vScale = 2; + videomodes.hShift = 160; + videomodes.vShift = 35; + videomodes.dOffset = 464; + PLAYER_MIN_Z = 321; + PLAYER_MAX_Z = 465; + BGHEIGHT = 321; + break; + + // 720x480 - DVD + case 3: + videomodes.hRes = 720; + videomodes.vRes = 480; + videomodes.hScale = 2.25; + videomodes.vScale = 2; + videomodes.hShift = 200; + videomodes.vShift = 35; + videomodes.dOffset = 464; + PLAYER_MIN_Z = 321; + PLAYER_MAX_Z = 465; + BGHEIGHT = 321; + break; + + // 800x480 - WVGA + case 4: + videomodes.hRes = 800; + videomodes.vRes = 480; + videomodes.hScale = 2.5; + videomodes.vScale = 2; + videomodes.hShift = 240; + videomodes.vShift = 35; + videomodes.dOffset = 464; + PLAYER_MIN_Z = 321; + PLAYER_MAX_Z = 465; + BGHEIGHT = 321; + break; + + // 800x600 - SVGA + case 5: + videomodes.hRes = 800; + videomodes.vRes = 600; + videomodes.hScale = 2.5; + videomodes.vScale = 2.5; + videomodes.hShift = 240; + videomodes.vShift = 44; + videomodes.dOffset = 580; + PLAYER_MIN_Z = 401; + PLAYER_MAX_Z = 582; + BGHEIGHT = 401; + break; + + // 960x540 - qHD + case 6: + videomodes.hRes = 960; + videomodes.vRes = 540; + videomodes.hScale = 3; + videomodes.vScale = 2.25; + videomodes.hShift = 320; + videomodes.vShift = 40; + videomodes.dOffset = 522; + PLAYER_MIN_Z = 362; + PLAYER_MAX_Z = 524; + BGHEIGHT = 362; + break; + + // Custom video mode + case 255: + videomodes.dOffset = videomodes.vRes * 0.9625; + printf("\nUsing debug video mode: %d x %d\n", videomodes.hRes, videomodes.vRes); + break; + default: + borShutdown(1, "Invalid video mode: %d in 'data/video.txt', supported modes:\n" + "0 - 320x240\n" + "1 - 480x272\n" + "2 - 640x480\n" + "3 - 720x480\n" + "4 - 800x480\n" + "5 - 800x600\n" + "6 - 960x540\n\n", videoMode); + break; + } + + video_stretch(savedata.stretch); + + if((vscreen = allocscreen(videomodes.hRes, videomodes.vRes, PIXEL_32)) == NULL) + { + borShutdown(1, "Not enough memory!\n"); + } + videomodes.pixel = pixelbytes[(int)vscreen->pixelformat]; + //video_set_mode(videomodes); + clearscreen(vscreen); + + if(log) + { + printf("Initialized video............\t%dx%d (Mode: %d)\n\n", videomodes.hRes, videomodes.vRes, videoMode); + } +} + + + +// ---------------------------------------------------------------------------- + + +// Set key or button safely (with switching) +void safe_set(int *arr, int index, int newkey, int oldkey) +{ + int i; + for(i = 0; i < 12; i++) + { + if(arr[i] == newkey) + { + arr[i] = oldkey; + } + } + arr[index] = newkey; +} + +void keyboard_setup(int player) +{ + int quit = 0; + int sdid = 0; + int selector = 0; + int setting = -1; + int i = 0; + int k = 0; + int ok = 0; + int disabledkey[MAX_BTN_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + int col1 = -8; + int col2 = 6; + + ptrdiff_t voffset; + ptrdiff_t pos; + + char* buf; + char* command; + char* filename = "translation/menu.txt"; + + char buttonnames[MAX_BTN_NUM][32]; + + size_t size; + ArgList arglist; + + char argbuf[MAX_ARG_LEN + 1] = ""; + int OPTIONS_NUM = MAX_BTN_NUM + 3; + + screen_status |= IN_SCREEN_BUTTON_CONFIG_MENU; + + printf("Loading control settings.......\t"); + + strcpy(buttonnames[SDID_MOVEUP], "Move Up"); + strcpy(buttonnames[SDID_MOVEDOWN], "Move Down"); + strcpy(buttonnames[SDID_MOVELEFT], "Move Left"); + strcpy(buttonnames[SDID_MOVERIGHT], "Move Right"); + strcpy(buttonnames[SDID_ATTACK], "Attack 1"); + strcpy(buttonnames[SDID_ATTACK2], "Attack 2"); + strcpy(buttonnames[SDID_ATTACK3], "Attack 3"); + strcpy(buttonnames[SDID_ATTACK4], "Attack 4"); + strcpy(buttonnames[SDID_JUMP], "Jump"); + strcpy(buttonnames[SDID_SPECIAL], "Special"); + strcpy(buttonnames[SDID_START], "Start"); + strcpy(buttonnames[SDID_SCREENSHOT], "Screenshot"); + strcpy(buttonnames[SDID_ESC], "Exit"); + + savesettings(); + bothnewkeys = 0; + + /* + Kratus (10-2021) Added an alternative location for the translation file, now it's possible to use in an external folder + Now the modder can load exported translation files by using "filestream" script functions + Useful for creating custom translations without unpack the game + The default engine translation location will be maintained for backward compatibility + + Kratus (11-2021) Inverted the path priority, now the external file will override the internal file + Useful to maintain the english translation intact inside the pak file if no other language file is found in the external path + Otherwise you will need to rollback the english file every time another language is used and then removed + This operation is needed only if the english translation file uses some custom menu texts for english language too + */ + if(buffer_pakfile(filename, &buf, &size) != 1) + { + goto default_file; + } + else + { + goto proceed; + } + +default_file: + + if(buffer_pakfile("data/menu.txt", &buf, &size) != 1) + { + goto finish; + } + else + { + goto proceed; + } + +proceed: + + // Read file + // Now interpret the contents of buf line by line + pos = 0; + while(pos < size) + { + ParseArgs(&arglist, buf + pos, argbuf); + command = GET_ARG(0); + if(command[0]) + { + if(stricmp(command, "disablekey") == 0) + { + sdid = translate_SDID(GET_ARG(1)); + if(sdid >= 0) + { + disabledkey[sdid] = 1; + } + } + else if(stricmp(command, "renamekey") == 0) + { + sdid = translate_SDID(GET_ARG(1)); + if(sdid >= 0) + { + strcpy(buttonnames[sdid], GET_ARG(2)); + } + } + + } + // Go to next line + pos += getNewLineStart(buf + pos); + } + if(buf != NULL) + { + free(buf); + buf = NULL; + } + +finish: + + while(disabledkey[selector]) if(++selector > MAX_BTN_NUM - 1) break; + + while(!quit) + { + voffset = -6; + _menutextm(2, -8, 0, Tr("Player %i"), player + 1); + for(i = 0; i < MAX_BTN_NUM; i++) + { + if(!disabledkey[i]) + { + _menutext((selector == i), col1, voffset, "%s", buttonnames[i]); + _menutext((selector == i), col2, voffset, "%s", control_getkeyname(savedata.keys[player][i])); + voffset++; + } + } + + ++voffset; + if(savedata.joyrumble[player]) + { + _menutext((selector == OPTIONS_NUM - 3), col1, voffset++, Tr("Rumble Enabled")); + } + else + { + _menutext((selector == OPTIONS_NUM - 3), col1, voffset++, Tr("Rumble Disabled")); + } + + _menutextm((selector == OPTIONS_NUM - 2), ++voffset, 0, Tr("OK")); + _menutextm((selector == OPTIONS_NUM - 1), ++voffset, 0, Tr("Cancel")); + _menutextm((selector == OPTIONS_NUM), ++voffset, 0, Tr("Default")); + update((level != NULL), 0); + + if(setting > -1) + { + if(bothnewkeys & FLAG_ESC) + { + savedata.keys[player][setting] = ok; + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 50); + setting = -1; + } + if(setting > -1) + { + k = control_scankey(); + if(k) + { + safe_set(savedata.keys[player], setting, k, ok); + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + setting = -1; + // Prevent accidental screenshot + bothnewkeys = 0; + } + } + } + else + { + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + do + { + if(--selector < 0) + { + break; + } + } + while(selector < MAX_BTN_NUM && disabledkey[selector]); + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + do + { + if(++selector > MAX_BTN_NUM - 1) break; + } + while(disabledkey[selector]); + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + if(selector < 0) + { + selector = OPTIONS_NUM; + } + if(selector > OPTIONS_NUM) + { + selector = 0; + while(disabledkey[selector]) if(++selector > MAX_BTN_NUM - 1) break; + } + + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + + if (selector != OPTIONS_NUM - 3 && + bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT)) continue; + + if(selector == OPTIONS_NUM - 3) + { + savedata.joyrumble[player] ^= 1; + } + else if(selector == OPTIONS_NUM - 2) // OK + { + quit = 2; + } + else if(selector == OPTIONS_NUM - 1) // cancel + { + quit = 1; + } + else if(selector == OPTIONS_NUM) // default + { + clearbuttons(player); + } + else + { + setting = selector; + ok = savedata.keys[player][setting]; + savedata.keys[player][setting] = 0; + keyboard_getlastkey(); + } + } + } + } + + if(quit == 2) + { + apply_controls(); + savesettings(); + } + else + { + loadsettings(); + } + + screen_status &= ~IN_SCREEN_BUTTON_CONFIG_MENU; + + update(0, 0); + bothnewkeys = 0; + printf("Done!\n"); +} + +void menu_options_input() +{ + int quit = 0; + int selector = 1; // 0 + int x_pos = -6; + #if ANDROID + int OPTIONS_NUM = 6; + #else + int OPTIONS_NUM = 5; + #endif + + screen_status |= IN_SCREEN_CONTROL_OPTIONS_MENU; + bothnewkeys = 0; + + // Kratus (10-2021) Added a second instance of the "control_init" function while in the Control Options + // Useful to refresh some text translation if the language is changed "on-the-fly" and re-detect all active controls + control_init(savedata.usejoy); + + while(!quit) + { + _menutextm(2, x_pos-1, 0, Tr("Control Options")); + if(savedata.usejoy) + { + _menutext((selector == 0), x_pos, -2, Tr("GamePads Enabled")); + if(!control_getjoyenabled()) + { + _menutext((selector == 0), x_pos+11, -2, Tr(" - Device Not Ready")); + } + } + else + { + _menutext((selector == 0), x_pos, -2, Tr("GamePads Disabled")); + } + _menutext((selector == 1), x_pos,-1, Tr("Setup Player 1...")); + _menutext((selector == 2), x_pos, 0, Tr("Setup Player 2...")); + _menutext((selector == 3), x_pos, 1, Tr("Setup Player 3...")); + _menutext((selector == 4), x_pos, 2, Tr("Setup Player 4...")); + #if ANDROID + if(savedata.is_touchpad_vibration_enabled) + { + _menutextm((selector == 5), 4, 0, Tr("Touchpad Vibration Enabled")); + } + else + { + _menutextm((selector == 5), 4, 0, Tr("Touchpad Vibration Disabled")); + } + _menutextm((selector == 6), 6, 0, Tr("Back")); + #else + _menutextm((selector == 5), 5, 0, Tr("Back")); + #endif + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(selector < 0) + { + selector = OPTIONS_NUM; + } + if(selector > OPTIONS_NUM) + { + selector = 0; + } + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + switch(selector) + { + case 0: + control_usejoy((savedata.usejoy ^= 1)); + break; + case 1: + keyboard_setup(0); + break; + case 2: + keyboard_setup(1); + break; + case 3: + keyboard_setup(2); + break; + case 4: + keyboard_setup(3); + break; + #if ANDROID + case 5: + savedata.is_touchpad_vibration_enabled ^= 1; + break; + #endif + default: + quit = (bothnewkeys & FLAG_ANYBUTTON); + } + } + } + savesettings(); + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_CONTROL_OPTIONS_MENU; +} + + +/* + Kratus (02-2023) Readjusted the master / effectvol / musicvol range and increment + Round values like 100 by default make more sense than some like 70/120 + So, I transferred the hardcoded volume adjustment to the "soundmix" code instead of changing the default volumes + Master now goes from 0 to 100 and default to 100 + Effects (samples) now goes from 0 to 100 and default to 100 + Music now goes from 0 to 200 and default to 100 +*/ +void menu_options_sound() +{ + int quit = 0; + int selector = 0; + int dir; + int col1 = -8; + int col2 = 6; + + screen_status |= IN_SCREEN_SOUND_OPTIONS_MENU; + bothnewkeys = 0; + + while(!quit) + { + _menutextm(2, -5, 0, Tr("Sound Options")); + _menutext((selector == 0), col1, -2, Tr("Master Volume:")); // Kratus (02-2023) Renamed from Sound to Master volume + _menutext((selector == 0), col2, -2, "%i", savedata.soundvol); + _menutext((selector == 1), col1, -1, Tr("Effects Volume:")); // Kratus (02-2023) Renamed from SFX to Effects volume + _menutext((selector == 1), col2, -1, "%i", savedata.effectvol); + _menutext((selector == 2), col1, 0, Tr("Music Volume:")); + _menutext((selector == 2), col2, 0, "%i", savedata.musicvol); + _menutext((selector == 3), col1, 1, Tr("BGM:")); + _menutext((selector == 3), col2, 1, (savedata.usemusic ? Tr("Enabled") : Tr("Disabled"))); + _menutext((selector == 4), col1, 2, Tr("Show Titles:")); + _menutext((selector == 4), col2, 2, (savedata.showtitles ? Tr("Yes") : Tr("No"))); + _menutextm((selector == 5), 5, 0, Tr("Back")); + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(selector < 0) + { + selector = 5; + } + if(selector > 5) + { + selector = 0; + } + + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + dir = 0; + + if(bothnewkeys & FLAG_MOVELEFT) + { + dir = -1; + } + else if(bothnewkeys & FLAG_MOVERIGHT) + { + dir = 1; + } + + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + // Kratus (02-2023) Reworked the old and unuseful "soundvol" to act as the Master Volume for both sound/music + switch(selector) + { + case 0: + savedata.soundvol += 5 * dir; + if(savedata.soundvol < 0) + { + savedata.soundvol = 0; + } + if(savedata.soundvol > 100) + { + savedata.soundvol = 100; + } + SB_setvolume(SB_VOICEVOL, savedata.soundvol); + + // Kratus (02-2023) Synchronize sound/music volumes to master + savedata.effectvol = savedata.soundvol; + savedata.musicvol = savedata.soundvol; + break; + case 1: + savedata.effectvol += 5 * dir; + if(savedata.effectvol < 0) + { + savedata.effectvol = 0; + } + if(savedata.effectvol > 100) + { + savedata.effectvol = 100; + } + break; + case 3: + if(!dir) + { + break; + } + if(!savedata.usemusic) + { + savedata.usemusic = 1; + music("data/music/remix", 1, 0); + } + else + { + savedata.usemusic = 0; + sound_close_music(); + } + break; + case 2: + savedata.musicvol += 5 * dir; + if(savedata.musicvol < 0) + { + savedata.musicvol = 0; + } + if(savedata.musicvol > 200) + { + savedata.musicvol = 200; + } + sound_volume_music(savedata.musicvol, savedata.musicvol); + break; + case 4: + savedata.showtitles = !savedata.showtitles; + break; + default: + quit = 1; + } + } + } + savesettings(); + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_SOUND_OPTIONS_MENU; +} + +void menu_options_config() // OX. Load from / save to default.cfg. Restore OpenBoR "factory" settings. +{ + int quit = 0; + int selector = 0; + int saved = 0; + int loaded = 0; + int restored = 0; + + bothnewkeys = 0; + + // Kratus (11-2021) Removed spaces on the " Done!" text to fix translation + while(!quit) + { + _menutextm(2, -5, 0, Tr("Configuration Settings")); + + if(saved == 1) + { + _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), Tr(" Done!")); + } + else + { + _menutextm((selector == 0), -3, 0, Tr("Save Settings To Default.cfg%s"), ""); + } + + if(loaded == 1) + { + _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), Tr(" Done!")); + } + else + { + _menutextm((selector == 1), -2, 0, Tr("Load Settings From Default.cfg%s"), ""); + } + + if(restored == 1) + { + _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), Tr(" Done!")); + } + else + { + _menutextm((selector == 2), -1, 0, Tr("Restore OpenBoR Defaults%s"), ""); + } + + _menutextm((selector == 3), 6, 0, Tr("Back")); + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + + if(selector < 0) + { + selector = 3; + } + if(selector > 3) + { + selector = 0; + } + + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + switch(selector) + { + case 0: + saveasdefault(); + saved = 1; + break; + + case 1: + loadfromdefault(); + //borShutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n"); + init_videomodes(0); + if(!video_set_mode(videomodes)) + { + borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes); + } + SB_setvolume(SB_VOICEVOL, savedata.soundvol); + sound_volume_music(savedata.musicvol, savedata.musicvol); + loaded = 1; + break; + case 2: + clearsettings(); + //borShutdown(2, "\nSettings Loaded From Default.cfg. Restart Required.\n\n"); + init_videomodes(0); + if(!video_set_mode(videomodes)) + { + borShutdown(1, "Unable to set video mode: %d x %d!\n", videomodes.hRes, videomodes.vRes); + } + SB_setvolume(SB_VOICEVOL, savedata.soundvol); + sound_volume_music(savedata.musicvol, savedata.musicvol); + restored = 1; + break; + default: + quit = 1; + } + } + } + savesettings(); + bothnewkeys = 0; +} + +/* +* Caskey, Damon V. +* 2026-07-06 +* +* Return menu display text for a collision debug +* display pair. +*/ +static char* collision_debug_type_string(e_debug_display debug_field, e_debug_display flat_bit, e_debug_display projected_bit) { + + if ((debug_field & flat_bit) && (debug_field & projected_bit)) { + return Tr("Flat + Projected"); + } else if (debug_field & flat_bit) { + return Tr("Flat"); + } else if (debug_field & projected_bit) { + return Tr("Projected"); + } else { + return Tr("Disabled"); + } +} + +/* +* Caskey, Damon V. +* 2026-07-06 +* +* Cycle collision debug display mode for one collision type. +* +* Each collision type uses two debug bits: +* +* flat_bit = draw the normal 2D rectangle overlay. +* projected_bit = draw the projected 3D cube overlay. +* +* The two bits combine into four possible states: +* +* 0 = None +* 1 = Flat +* 2 = Projected +* 3 = Flat + Projected +* +* Direction controls cycle order: +* +* TOGGLE_DIRECTION_BACKWARD = cycle backward. +* TOGGLE_DIRECTION_FORWARD = cycle forward. +* +* Return value is the updated debug bitfield. +*/ +static e_debug_display collision_debug_type_cycle(const e_debug_display debug_field, const e_debug_display flat_bit, const e_debug_display projected_bit, const e_menu_toggle_direction toggle_direction) { + + /* + * Local only. Actual state is controlled by + * the savedata.debuginfo bitfield. This just + * gives us a simple linear value to work with + * for cycling so we do't need a massive if/else + * chain. + */ + #define COLLISION_DEBUG_DISPLAY_NONE 0 + #define COLLISION_DEBUG_DISPLAY_FLAT 1 + #define COLLISION_DEBUG_DISPLAY_PROJECTED 2 + #define COLLISION_DEBUG_DISPLAY_BOTH 3 + + /* + * Int scratchpads. These are enum values, but compiler + * will throw a fit if we try to use them in operations + * that cause implicit integer promotion. + */ + int state; + int debug_flags; + + /* + * Work in an int scratch value because enum values are + * still bitfields, and bitwise operators promote them + * to int anyway. + */ + debug_flags = debug_field; + + /* + * Convert current bitfield state into a compact + * display mode value. + * + * Bit 0 = flat display enabled. + * Bit 1 = projected display enabled. + */ + state = COLLISION_DEBUG_DISPLAY_NONE; + + if (debug_flags & flat_bit) { + state |= COLLISION_DEBUG_DISPLAY_FLAT; + } + + if (debug_flags & projected_bit) { + state |= COLLISION_DEBUG_DISPLAY_PROJECTED; + } + + /* + * Step through the four display modes. + * + * Backward: + * None <- Flat <- Projected <- Both <- None + * + * Forward: + * None -> Flat -> Projected -> Both -> None + */ + switch(toggle_direction) { + case TOGGLE_DIRECTION_BACKWARD: + state--; + + if (state < COLLISION_DEBUG_DISPLAY_NONE) { + state = COLLISION_DEBUG_DISPLAY_BOTH; + } + + break; + + case TOGGLE_DIRECTION_FORWARD: + default: + state++; + + if (state > COLLISION_DEBUG_DISPLAY_BOTH) { + state = COLLISION_DEBUG_DISPLAY_NONE; + } + + break; + } + + /* + * Clear only the two bits managed by this collision + * debug option. Leave all other debug flags untouched. + */ + debug_flags &= ~(flat_bit | projected_bit); + + /* + * Expand compact state back into the debug bitfield. + */ + if (state & COLLISION_DEBUG_DISPLAY_FLAT) { + debug_flags |= flat_bit; + } + + if (state & COLLISION_DEBUG_DISPLAY_PROJECTED) { + debug_flags |= projected_bit; + } + + return (e_debug_display)debug_flags; + + #undef COLLISION_DEBUG_DISPLAY_NONE + #undef COLLISION_DEBUG_DISPLAY_FLAT + #undef COLLISION_DEBUG_DISPLAY_PROJECTED + #undef COLLISION_DEBUG_DISPLAY_BOTH +} + +void menu_options_debug() { + #define MENU_POS_Y -4 + #define MENU_ITEMS_MARGIN_Y 2 + #define COLUMN_1_POS_X -11 + #define COLUMN_2_POS_X COLUMN_1_POS_X + 14 + #define MENU_ITEM_FIRST_INDEX 0 + + // Selections enumerator. All + // selection items should be placed + // here first. + typedef enum { + + // First item can be + // whatever we like, + // but it must be set + // to the MENU_ITEM_FIRST_INDEX + // constant. + ITEM_PERFORMANCE = MENU_ITEM_FIRST_INDEX, + + // Items between the first + // and last can go in any + // order. + ITEM_POSITION, + ITEM_COL_ATTACK, + ITEM_COL_BODY, + ITEM_COL_SPACE, + ITEM_COL_RANGE, + + // This is the "Back" + // selection and should + // always be last. + ITEM_EXIT + } e_selections; + + int pos_y; + int quit = 0; + e_selections selector = 0; + bothnewkeys = 0; + + e_menu_toggle_direction toggle_direction = TOGGLE_DIRECTION_FORWARD; + + while(!quit) { + + // Display menu title. + _menutextm(2, MENU_POS_Y, 0, Tr("Debug Settings")); + + // Menu items. + // Y position is controlled by a incremented integer. + // Integer has a base value of the Menu title Y + // plus static offset. Below each item, this Y value + // increments by one. We can then add or remove + // menu items here and the on screen position will + // take care of itself without us needing to mess + // with a bunch of hard constants. + + // Reset menu item position Y. + pos_y = MENU_POS_Y + MENU_ITEMS_MARGIN_Y; + + _menutext((selector == ITEM_PERFORMANCE), COLUMN_1_POS_X, pos_y, Tr("Performance:")); + _menutext((selector == ITEM_PERFORMANCE), COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_PERFORMANCE ? Tr("Enabled") : Tr("Disabled"))); + pos_y++; + + _menutext((selector == ITEM_POSITION), COLUMN_1_POS_X, pos_y, Tr("Basic Properties:")); + _menutext((selector == ITEM_POSITION), COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_PROPERTIES ? Tr("Enabled") : Tr("Disabled"))); + pos_y++; + + _menutext((selector == ITEM_COL_ATTACK), COLUMN_1_POS_X, pos_y, Tr("Collision Attack:")); + _menutext((selector == ITEM_COL_ATTACK), COLUMN_2_POS_X, pos_y, collision_debug_type_string(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_ATTACK_2D, DEBUG_DISPLAY_COLLISION_ATTACK_3D)); + pos_y++; + + _menutext((selector == ITEM_COL_BODY), COLUMN_1_POS_X, pos_y, Tr("Collision Body:")); + _menutext((selector == ITEM_COL_BODY), COLUMN_2_POS_X, pos_y, collision_debug_type_string(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_BODY_2D, DEBUG_DISPLAY_COLLISION_BODY_3D)); + pos_y++; + + _menutext((selector == ITEM_COL_SPACE), COLUMN_1_POS_X, pos_y, Tr("Collision Space:")); + _menutext((selector == ITEM_COL_SPACE), COLUMN_2_POS_X, pos_y, collision_debug_type_string(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_SPACE_2D, DEBUG_DISPLAY_COLLISION_SPACE_3D)); + pos_y++; + + _menutext((selector == ITEM_COL_RANGE), COLUMN_1_POS_X, pos_y, Tr("Range:")); + _menutext((selector == ITEM_COL_RANGE), COLUMN_2_POS_X, pos_y, (savedata.debuginfo & DEBUG_DISPLAY_RANGE ? Tr("Enabled") : Tr("Disabled"))); + pos_y++; + + // Display exit title + _menutextm((selector == ITEM_EXIT), pos_y + (MENU_ITEMS_MARGIN_Y), 0, Tr("Back")); + + // Run an engine update. + update((level != NULL), 0); + + // If user presses up/down or esc, let's act accordingly. + if(bothnewkeys & (FLAG_MOVEUP | FLAG_MOVEDOWN | FLAG_ESC)) { + + // If user presses escape, then set quit + // flag immediately. Else wise, increment + // or decrement selector as needed. + if(bothnewkeys & FLAG_ESC) { + quit = 1; + + } else if(bothnewkeys & FLAG_MOVEUP) { + // Play beep if available. + // Kratus (04-2022) Moved the BEEP sound to work only with UP/DOWN keys + if(global_sample_list.beep >= 0) { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + // If we are at the top item, loop + // to last. Otherwise, move one up. + if(selector <= MENU_ITEM_FIRST_INDEX) { + selector = ITEM_EXIT; + } else { + --selector; + } + + } else if(bothnewkeys & FLAG_MOVEDOWN) { + + // Play beep if available. + // Kratus (04-2022) Moved the BEEP sound to work only with UP/DOWN keys + if(global_sample_list.beep >= 0) { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + // If we are at the last item + // (which should be "back"), then + // loop back to first. Otherwise + // move one down. + if(selector >= ITEM_EXIT) { + selector = MENU_ITEM_FIRST_INDEX; + + } else { + ++selector; + } + } + } + + + // Toggle selection value on left/right or + // trigger button press. + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) { + + if(global_sample_list.beep_2 >= 0) { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + toggle_direction = (bothnewkeys & FLAG_MOVELEFT) ? TOGGLE_DIRECTION_BACKWARD : TOGGLE_DIRECTION_FORWARD; + + // This is where menu items are executed. + switch(selector) { + case ITEM_PERFORMANCE: + savedata.debuginfo ^= DEBUG_DISPLAY_PERFORMANCE; + break; + case ITEM_POSITION: + savedata.debuginfo ^= DEBUG_DISPLAY_PROPERTIES; + break; + case ITEM_COL_ATTACK: + savedata.debuginfo = collision_debug_type_cycle(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_ATTACK_2D, DEBUG_DISPLAY_COLLISION_ATTACK_3D, toggle_direction); + break; + case ITEM_COL_BODY: + savedata.debuginfo = collision_debug_type_cycle(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_BODY_2D, DEBUG_DISPLAY_COLLISION_BODY_3D, toggle_direction); + break; + case ITEM_COL_SPACE: + savedata.debuginfo = collision_debug_type_cycle(savedata.debuginfo, DEBUG_DISPLAY_COLLISION_SPACE_2D, DEBUG_DISPLAY_COLLISION_SPACE_3D, toggle_direction); + break; + case ITEM_COL_RANGE: + savedata.debuginfo ^= DEBUG_DISPLAY_RANGE; + break; + case ITEM_EXIT: + quit = 1; + } + } + } + + savesettings(); + bothnewkeys = 0; + + #undef MENU_POS_Y + #undef MENU_ITEMS_MARGIN_Y + #undef COLUMN_1_POS_X + #undef COLUMN_2_POS_X + #undef MENU_ITEM_FIRST_INDEX +} + +/* +* Caskey, Damon V. +* 2022-05-05 +* +* Display cheat options menu and +* apply player selections. +*/ +void menu_options_cheats() +{ +#define MENU_POS_Y -4 +#define MENU_ITEMS_MARGIN_Y 2 +#define COLUMN_1_POS_X -11 +#define COLUMN_2_POS_X COLUMN_1_POS_X + 14 + + typedef struct s_option_list + { + e_cheat_options active; + e_cheat_options menu; + char label[20]; + } s_option_list; + + int option_list_count = 7; + s_option_list option_list[] = { + {.active = CHEAT_OPTIONS_IMPLACABLE_ACTIVE, .menu = CHEAT_OPTIONS_IMPLACABLE_MENU, .label = "Implacable March:" }, + {.active = CHEAT_OPTIONS_CREDITS_ACTIVE, .menu = CHEAT_OPTIONS_CREDITS_MENU, .label = "Infinite Credits:" }, + {.active = CHEAT_OPTIONS_ENERGY_ACTIVE, .menu = CHEAT_OPTIONS_ENERGY_MENU, .label = "Infinite Energy:" }, + {.active = CHEAT_OPTIONS_HEALTH_ACTIVE, .menu = CHEAT_OPTIONS_HEALTH_MENU, .label = "Infinite Health:" }, + {.active = CHEAT_OPTIONS_LIVES_ACTIVE, .menu = CHEAT_OPTIONS_LIVES_MENU, .label = "Infinite Lives:" }, + {.active = CHEAT_OPTIONS_MULTIHIT_ACTIVE, .menu = CHEAT_OPTIONS_MULTIHIT_MENU, .label = "Multihit Glitch:" }, + {.active = CHEAT_OPTIONS_TOD_ACTIVE, .menu = CHEAT_OPTIONS_TOD_MENU, .label = "Touch of Death:" } + }; + + int option_list_cursor = 0; + int option_available_count = 0; + int option_available_first = 0; + int pos_y = 0; + int quit = 0; + int selector = 0; + + bothnewkeys = 0; + + screen_status |= IN_SCREEN_CHEAT_OPTIONS_MENU; + + //printf("\n\n menu_options_cheats()"); + + while (!quit) + { + /* Display menu title. */ + _menutextm(2, MENU_POS_Y, 0, Tr("Cheat Options")); + + /* + * Reset menu item position Y and + * valid menu item count. + */ + option_available_count = 0; + option_available_first = 0; + pos_y = MENU_POS_Y + MENU_ITEMS_MARGIN_Y; + + /* + * Iterate through the option list + * array and display available items. + * + * At each valid option, increment + * option count and Y position. + */ + + for (option_list_cursor = 0; option_list_cursor < option_list_count; option_list_cursor++) + { + if (global_config.cheats & option_list[option_list_cursor].menu) + { + /* + * If option available count is still 0, this + * is the first available option. Record the + * index for use downstream. + */ + + if (!option_available_count) + { + option_available_first = option_list_cursor; + } + + /* Print the menu text and on/off status. */ + _menutext((selector == option_list_cursor), COLUMN_1_POS_X, pos_y, Tr(option_list[option_list_cursor].label)); + _menutext((selector == option_list_cursor), COLUMN_2_POS_X, pos_y, (global_config.cheats & option_list[option_list_cursor].active ? Tr("On") : Tr("Off"))); + + option_available_count++; + pos_y++; + } + } + + /* No items avaialble. Alert player. */ + if (!option_available_count) + { + _menutextm(0, pos_y, 0, Tr("No cheats available.")); + } + + /* + * Add the exit option. We can use the array size + * for a selector index due to 0 indexing. + */ + + pos_y++; + _menutextm((selector == option_list_count), pos_y, 0, Tr("Back")); + + /* Run an engine update. */ + update((level != NULL), 0); + + /* + * Handle user input and populate selector to + * appropriate value. + * + * First we set selector to first item in case + * the first available's index is not 0. + * + * Next, handle buttons. ESC quits the menu + * instantly. Up/Down select target item. + * Left/Right/Action buttons toggle value. + */ + + if (!selector && option_available_first) + { + selector = option_available_first; + } + + if (bothnewkeys & FLAG_ESC) + { + quit = 1; + } + + if (bothnewkeys & FLAG_MOVEUP) + { + /* Play beep if available. */ + if (global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + /* Wrap around if needed. */ + if (selector <= option_available_first) + { + selector = option_list_count; + } + else + { + selector--; + } + + /* Skip disabled items. */ + while (!(global_config.cheats & option_list[selector].menu) && selector > option_available_first) + { + selector--; + } + } + + if (bothnewkeys & FLAG_MOVEDOWN) + { + /* Play beep if available. */ + if (global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + /* Wrap around if needed. */ + if (selector >= option_list_count) + { + selector = option_available_first; + } + else + { + selector++; + } + + /* Skip disabled items. */ + while (!(global_config.cheats & option_list[selector].menu) && selector < option_list_count) + { + selector++; + } + } + + /* + * Toggle selection value on left/right or + * trigger button press. + */ + if (bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + if (global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + /* + * Take action based on selector. + * + * The last option is the exit item, so if that's + * our selector value we exit this menu. + * + * Otherwise use selector as array key to get the + * bit we want to toggle in our target value. + */ + + if (selector < option_list_count) + { + global_config.cheats ^= option_list[selector].active; + } + else + { + quit = 1; + } + } + } + + //savesettings(); + + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_CHEAT_OPTIONS_MENU; + +#undef MENU_POS_Y +#undef MENU_ITEMS_MARGIN_Y +#undef COLUMN_1_POS_X +#undef COLUMN_2_POS_X +} + +void menu_options_system() +{ + #define SYS_OPT_Y_POS -4 + + enum { + SYS_OPT_LOG, + SYS_OPT_VSDAMAGE, + SYS_OPT_CHEATS, + SYS_OPT_DEBUG, + SYS_OPT_CONFIG, + SYS_OPT_BACK + }; + + int quit = 0; + int selector = 0; + int col1 = -11; + int col2 = col1+14; + int ex_labels = 0; + int RET = SYS_OPT_BACK; + int line = 0; + + screen_status |= IN_SCREEN_SYSTEM_OPTIONS_MENU; + bothnewkeys = 0; + if (nodebugoptions) ex_labels = 1; + RET -= ex_labels; + + while(!quit) + { + line = 0; + + _menutextm(2, SYS_OPT_Y_POS-2, 0, Tr("System Options")); + + _menutext(0, col1, SYS_OPT_Y_POS + line, Tr("Total RAM:")); + _menutext(0, col2, SYS_OPT_Y_POS + line, Tr("%s KB"), commaprint(getSystemRam(KBYTES))); + line++; + + _menutext(0, col1, SYS_OPT_Y_POS + line, Tr("Used RAM:")); + _menutext(0, col2, SYS_OPT_Y_POS + line, Tr("%s KB"), commaprint(getUsedRam(KBYTES))); + line++; + + _menutext(0, col1, SYS_OPT_Y_POS + line, Tr("Max Players:")); + _menutext(0, col2, SYS_OPT_Y_POS + line, Tr("%i"), levelsets[current_set].maxplayers); + line++; + + _menutext((selector == SYS_OPT_LOG), col1, SYS_OPT_Y_POS + line, Tr("File Logging:")); + _menutext((selector == SYS_OPT_LOG), col2, SYS_OPT_Y_POS + line, (savedata.uselog ? Tr("Enabled") : Tr("Disabled"))); + line++; + + _menutext((selector == SYS_OPT_VSDAMAGE), col1, SYS_OPT_Y_POS + line, Tr("Versus Damage:"), 0); + if(versusdamage == 0) + { + _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS + line, Tr("Disabled by Module")); + } + else if(versusdamage == 1) + { + _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS + line, Tr("Enabled by Module")); + } + else + { + if(savedata.mode) + { + _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS + line, Tr("Disabled")); //Mode 1 - Players CAN'T attack each other + } + else + { + _menutext((selector == SYS_OPT_VSDAMAGE), col2, SYS_OPT_Y_POS + line, Tr("Enabled")); //Mode 2 - Players CAN attack each other + } + } + line++; + + if (global_config.cheats & CHEAT_OPTIONS_MASTER_MENU) + { + _menutext((selector == SYS_OPT_CHEATS), col1, SYS_OPT_Y_POS + line, Tr("Cheat Options...")); + line++; + } + + if (!nodebugoptions) + { + _menutext((selector == SYS_OPT_DEBUG), col1, SYS_OPT_Y_POS + line, Tr("Debug Settings...")); + line++; + } + + _menutext((selector == SYS_OPT_CONFIG), col1, SYS_OPT_Y_POS + line, Tr("Config Settings...")); + + /* Extra lines for spacing. */ + line += 2; + + _menutextm((selector == RET), SYS_OPT_Y_POS + line, 0, Tr("Back")); + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + + /* Skip disabled items. */ + + if (selector == SYS_OPT_CHEATS && !(global_config.cheats & CHEAT_OPTIONS_MASTER_MENU)) + { + --selector; + } + + if (selector == SYS_OPT_DEBUG && nodebugoptions) + { + --selector; + } + + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + + /* Skip disabled items. */ + + if (selector == SYS_OPT_CHEATS && !(global_config.cheats & CHEAT_OPTIONS_MASTER_MENU)) + { + ++selector; + } + + if (selector == SYS_OPT_DEBUG && nodebugoptions) + { + ++selector; + } + + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(selector < 0) + { + selector = RET; + } + if(selector > RET) + { + selector = 0; + } + + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + + if (selector == RET) quit = 1; + else if (selector == SYS_OPT_LOG) savedata.uselog = !savedata.uselog; + else if (selector == SYS_OPT_VSDAMAGE) + { + if (versusdamage > 1) + { + if (savedata.mode) + { + savedata.mode = 0; + } + else + { + savedata.mode = 1; + } + } + } + else if (selector == SYS_OPT_CHEATS) + { + menu_options_cheats(); + } + else if (selector == SYS_OPT_DEBUG && !nodebugoptions) + { + menu_options_debug(); + } + else if (selector == SYS_OPT_CHEATS) + { + menu_options_cheats(); + } + else if (selector == SYS_OPT_DEBUG && !nodebugoptions) menu_options_debug(); + else if (selector==SYS_OPT_CONFIG) menu_options_config(); + else quit = 1; + } + } + savesettings(); + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_SYSTEM_OPTIONS_MENU; + + #undef SYS_OPT_Y_POS +} + + +void menu_options_video() +{ + int quit = 0; + int selector = 0; + int dir; + int col1 = -15, col2 = 1; + + screen_status |= IN_SCREEN_VIDEO_OPTIONS_MENU; + bothnewkeys = 0; + + while(!quit) + { + _menutextm(2, -5, 0, Tr("Video Options")); + _menutext((selector == 0), col1, -3, Tr("Brightness:")); + _menutext((selector == 0), col2, -3, "%i", savedata.brightness); + _menutext((selector == 1), col1, -2, Tr("Gamma:")); + _menutext((selector == 1), col2, -2, "%i", savedata.gamma); + _menutext((selector == 2), col1, -1, Tr("Window Offset:")); + _menutext((selector == 2), col2, -1, "%i", savedata.windowpos); + +#if SDL + _menutext((selector == 3), col1, 0, Tr("Display Mode:")); + _menutext((selector == 3), col2, 0, savedata.fullscreen ? Tr("Full") : Tr("Window")); + + _menutext((selector == 4), col1, 1, Tr("Video Backend:")); + _menutext((selector == 4), col2, 1, (opengl ? Tr("OpenGL") : Tr("SDL"))); + + _menutext((selector == 5), col1, 2, Tr("Scale:")); +#ifdef ANDROID + if(savedata.hwscale == 0) +#else + if(savedata.fullscreen) +#endif + { + _menutext((selector == 5), col2, 2, Tr("Automatic")); + } + else + { + _menutext((selector == 5), col2, 2, "%4.2fx - %ix%i", savedata.hwscale, (int)(videomodes.hRes * savedata.hwscale), (int)(videomodes.vRes * savedata.hwscale)); + } + + _menutext((selector == 6), col1, 3, Tr("Hardware Filter:")); + { + char *filterName; + if (savedata.hwscale == 1.0 && !savedata.fullscreen) + filterName = "Disabled"; + else if (opengl) + filterName = "High Quality"; + else if (savedata.hwfilter) + filterName = "Simple"; + else + filterName = "Bilinear"; + _menutext((selector == 6), col2, 3, Tr(filterName)); + } + + _menutext((selector == 7), col1, 4, Tr("Software Filter:")); + _menutext((selector == 7), col2, 4, ((savedata.hwscale >= 2.0 || savedata.fullscreen) ? Tr(GfxBlitterNames[savedata.swfilter]) : Tr("Disabled"))); + + char fpslimit_text[32]; + switch (savedata.fpslimit) + { + case 0: + snprintf(fpslimit_text, sizeof(fpslimit_text), "%s", Tr("Disabled")); + break; + case 1: + snprintf(fpslimit_text, sizeof(fpslimit_text), "%i (VSync)", video_current_refresh_rate()); + break; + case 2: + snprintf(fpslimit_text, sizeof(fpslimit_text), "%i", 200); + break; + case 3: + snprintf(fpslimit_text, sizeof(fpslimit_text), "%i", 500); + break; + default: + snprintf(fpslimit_text, sizeof(fpslimit_text), "%s", Tr("Unknown")); + break; + } + + _menutext((selector == 8), col1, 5, Tr("FPS Limit:")); + _menutext((selector == 8), col2, 5, fpslimit_text); + + if(savedata.fullscreen) + { + _menutext((selector == 9), col1, 6, Tr("Fullscreen Type:")); + _menutext((selector == 9), col2, 6, (savedata.stretch ? Tr("Stretch to Screen") : Tr("Preserve Aspect Ratio"))); + } + else if(selector == 9) + { + selector = (bothnewkeys & FLAG_MOVEUP) ? 8 : 10; + } + + _menutextm((selector == 10), 9, 0, Tr("Back")); + if(selector < 0) + { + selector = 10; + } + if(selector > 10) + { + selector = 0; + } +#endif + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + dir = 0; + + if(bothnewkeys & FLAG_MOVELEFT) + { + dir = -1; + } + else if(bothnewkeys & FLAG_MOVERIGHT) + { + dir = 1; + } + + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + switch(selector) + { + case 0: + savedata.brightness += 8 * dir; + if(savedata.brightness < -256) + { + savedata.brightness = -256; + } + if(savedata.brightness > 256) + { + savedata.brightness = 256; + } + vga_vwait(); + set_color_correction(savedata.gamma, savedata.brightness); + break; + case 1: + savedata.gamma += 8 * dir; + if(savedata.gamma < -256) + { + savedata.gamma = -256; + } + if(savedata.gamma > 256) + { + savedata.gamma = 256; + } + vga_vwait(); + set_color_correction(savedata.gamma, savedata.brightness); + break; + case 2: + savedata.windowpos += dir; + if(savedata.windowpos < -2) + { + savedata.windowpos = -2; + } + if(savedata.windowpos > 20) + { + savedata.windowpos = 20; + } + break; +#if SDL + case 3: + video_fullscreen_flip(); + break; + case 4: + savedata.usegl = !savedata.usegl; + video_set_mode(videomodes); + set_color_correction(savedata.gamma, savedata.brightness); + break; + case 5: +#ifndef ANDROID + if(savedata.fullscreen) + { + break; + } + savedata.hwscale += dir * 0.25; + if(savedata.hwscale < 0.25) + { + savedata.hwscale = 0.25; + } + if(savedata.hwscale > 4.00) + { + savedata.hwscale = 4.00; + } + video_set_mode(videomodes); +#else + savedata.hwscale += dir * 0.25; + if(savedata.hwscale < 0.0) + { + savedata.hwscale = 0.0; + } + if(savedata.hwscale > 4.00) + { + savedata.hwscale = 4.00; + } +#endif + case 6: +#ifndef ANDROID + if (opengl || (!savedata.fullscreen && savedata.hwscale == 1.0)) + { + break; + } +#endif + savedata.hwfilter += dir; + if(savedata.hwfilter < 0) + { + savedata.hwfilter = 1; + } + if(savedata.hwfilter > 1) + { + savedata.hwfilter = 0; + } + video_set_mode(videomodes); + break; + case 7: + if(!savedata.fullscreen && savedata.hwscale < 2.0) + { + break; + } + videomodes.filter += dir; + if(videomodes.filter > BLITTER_MAX - 1) + { + videomodes.filter = 0; + } + if(videomodes.filter < 0) + { + videomodes.filter = BLITTER_MAX - 1; + } + savedata.swfilter = videomodes.filter; + memset(pDeltaBuffer, 0x00, 1244160); + video_set_mode(videomodes); + break; + case 8: + savedata.fpslimit = (savedata.fpslimit + dir) % 4; + if (savedata.fpslimit < 0) savedata.fpslimit += 4; + video_set_mode(videomodes); + break; + case 9: + video_stretch((savedata.stretch ^= 1)); + break; +#endif + default: + quit = 1; + } + } + } + savesettings(); + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_VIDEO_OPTIONS_MENU; +} + + +void menu_options() +{ + #define OPT_Y_POS -1 + #define OPT_X_POS -7 + + typedef enum { + VIDEO_OPTION, + SOUND_OPTION, + CONTROL_OPTION, + SYSTEM_OPTION, + + END_OPTION + } e_selector; + + int quit = 0; + int y_offset = OPT_Y_POS; + int BACK_OPTION = END_OPTION; + e_selector selector = VIDEO_OPTION; + + screen_status |= IN_SCREEN_OPTIONS_MENU; + bothnewkeys = 0; + + while(!quit) + { + _menutextm((selector == VIDEO_OPTION), y_offset+VIDEO_OPTION, 0, Tr("Video Options...")); + _menutextm((selector == SOUND_OPTION), y_offset+SOUND_OPTION, 0, Tr("Sound Options...")); + _menutextm((selector == CONTROL_OPTION), y_offset+CONTROL_OPTION, 0, Tr("Control Options...")); + _menutextm((selector == SYSTEM_OPTION), y_offset+SYSTEM_OPTION, 0, Tr("System Options...")); + + _menutextm((selector == BACK_OPTION), y_offset+BACK_OPTION+2, 0, Tr("Back")); + + update((level != NULL), 0); + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + if(bothnewkeys & FLAG_MOVEUP) + { + if(selector <= VIDEO_OPTION) + { + selector = BACK_OPTION; + } + else --selector; + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(selector > BACK_OPTION) + { + selector = VIDEO_OPTION; + } + + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & (FLAG_MOVELEFT | FLAG_MOVERIGHT | FLAG_ANYBUTTON)) + { + + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + + if(selector==BACK_OPTION) quit = 1; + else if(selector==VIDEO_OPTION) menu_options_video(); + else if(selector==SOUND_OPTION) menu_options_sound(); + else if(selector==CONTROL_OPTION) menu_options_input(); + else if(selector==SYSTEM_OPTION) + { + menu_options_system(); + } + + else quit = 1; + } + } + savesettings(); + if(_pause == 1) + { + _pause = 2; + } + bothnewkeys = 0; + screen_status &= ~IN_SCREEN_OPTIONS_MENU; + + #undef TOT_CHEATS + #undef OPT_Y_POS + #undef OPT_X_POS + #undef CHEAT_PAUSE_POSY +} + +// ---------------------------------------------------------------------------- + + +void openborMain(int argc, char **argv) +{ + sprite_map = NULL; + int quit = 0; + int relback = 1; + int selector = 0; + u32 introtime = 0; + int started = 0; + char tmpBuff[MAX_BUFFER_LEN] = {""}; + int players[MAX_PLAYERS] = {0, 0, 0, 0}; + int i; + int argl; + + printf("OpenBoR %s, Compile Date: " __DATE__ "\n\n", VERSION); + + if(argc > 1) + { + argl = strlen(argv[1]); + if(argl > 14 && !memcmp(argv[1], "offscreenkill=", 14)) + { + DEFAULT_OFFSCREEN_KILL = getValidInt((char *)argv[1] + 14, "", ""); + } + if(argl > 14 && !memcmp(argv[1], "showfilesused=", 14)) + { + printFileUsageStatistics = getValidInt((char *)argv[1] + 14, "", ""); + } + } + + + modelcmdlist = createModelCommandList(); + modelstxtcmdlist = createModelstxtCommandList(); + levelcmdlist = createLevelCommandList(); + levelordercmdlist = createLevelOrderCommandList(); + createModelList(); + + // Load necessary components. + printf("Game Selected: %s\n\n", packfile); + loadsettings(); + startup(); + bothnewkeys = 0; + + if(skiptoset < 0) + { + + // New alternative background path. + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "logo"); + load_background(tmpBuff); + } + else + { + load_cached_background("data/bgs/logo"); + } + + while(_time < global_config.game_speed * 6 && !(bothnewkeys & (FLAG_ANYBUTTON | FLAG_ESC))) + { + update(0, 0); + } + + music("data/music/remix", 1, 0); + + // New alternative scene path. + if(custScenes != NULL) + { + strcpy(tmpBuff, custScenes); + strcat(tmpBuff, "logo.txt"); + playscene(tmpBuff); + } + else + { + playscene("data/scenes/logo.txt"); + } + } + clearscreen(background); + + while(!quit) + { + if(skiptoset < 0 && !(goto_mainmenu_flag&8)) + { + if(_time >= introtime) + { + // New alternative scene path. + if(custScenes != NULL) + { + strcpy(tmpBuff, custScenes); + strcat(tmpBuff, "intro.txt"); + playscene(tmpBuff); + } + else + { + playscene("data/scenes/intro.txt"); + } + update(0, 0); + introtime = _time + global_config.game_speed * 20; + relback = 1; + started = 0; + } + + if(bothnewkeys & FLAG_ESC) + { + quit = 1; + } + } + else + { + started = 1; + relback = 0; + } + + if (goto_mainmenu_flag != 0) goto_mainmenu_flag = 0; + if(!started) + { + if((_time % global_config.game_speed) < (global_config.game_speed / 2)) + { + _menutextm(0, 0, 0, Tr("PRESS START")); + } + if(bothnewkeys & (FLAG_ANYBUTTON)) + { + started = 1; + relback = 1; + } + } + else if(skiptoset >= 0) + { + loadGameFile(); + playgame(players, useSet >= 0 ? useSet : skiptoset, useSave); + } + else + { + _menutextm((selector == 0), 2, 0, Tr("Start Game")); + _menutextm((selector == 1), 3, 0, Tr("Options")); + _menutextm((selector == 2), 4, 0, Tr("How To Play")); + _menutextm((selector == 3), 5, 0, Tr("Hall Of Fame")); + _menutextm((selector == 4), 6, 0, Tr("Quit")); + if(selector < 0) + { + selector = 4; + } + if(selector > 4) + { + selector = 0; + } + + if(bothnewkeys) + { + introtime = _time + global_config.game_speed * 20; + } + + if(bothnewkeys & FLAG_MOVEUP) + { + --selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & FLAG_MOVEDOWN) + { + ++selector; + if(global_sample_list.beep >= 0) + { + sound_play_sample(global_sample_list.beep, 0, savedata.effectvol, savedata.effectvol, 100); + } + } + if(bothnewkeys & (FLAG_ANYBUTTON)) + { + if(global_sample_list.beep_2 >= 0) + { + sound_play_sample(global_sample_list.beep_2, 0, savedata.effectvol, savedata.effectvol, 100); + } + switch(selector) + { + case 0: + for(i = 0; i < MAX_PLAYERS; i++) + { + players[i] = player[i].newkeys & (FLAG_ANYBUTTON); + } + relback = choose_mode(players); + if(relback) + { + started = 0; + } + break; + case 1: + menu_options(); + break; + case 2: + { + int previousLoop = musicloop; + char previousMusic[sizeof(currentmusic)]; + + memset(previousMusic, 0, sizeof(previousMusic)); + strcpy(previousMusic, currentmusic); + + if(custScenes != NULL) + { + strcpy(tmpBuff, custScenes); + strcat(tmpBuff, "howto.txt"); + playscene(tmpBuff); + } + else + { + playscene("data/scenes/howto.txt"); + } + if(stricmp(previousMusic, currentmusic) != 0) + { + music(previousMusic, previousLoop, 0); + } + relback = 1; + break; + } + case 3: + hallfame(0); + relback = 1; + break; + default: + quit = 1; + break; + } + introtime = _time + global_config.game_speed * 20; + } + } + if(relback) + { + if(started) + { + /* + Kratus (10-2021) Added an additional instance of the translation function at menu screen + Used to refresh all text without close and reopen the engine + + Kratus (02-2023) Added an additional instance of the video_mode function at menu screen + Used to refresh the touchscreen properties on Android without restarting + */ + video_set_mode(videomodes); + ob_inittrans(); + + screen_status |= IN_SCREEN_MENU; + screen_status &= ~IN_SCREEN_TITLE; + + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "titleb"); + load_background(tmpBuff); + } + else + { + load_cached_background("data/bgs/titleb"); + } + } + else + { + screen_status &= ~IN_SCREEN_MENU; + screen_status |= IN_SCREEN_TITLE; + + if(custBkgrds != NULL) + { + strcpy(tmpBuff, custBkgrds); + strcat(tmpBuff, "title"); + load_background(tmpBuff); + } + else + { + load_cached_background("data/bgs/title"); + } + } + + if(!sound_query_music(NULL, NULL)) + { + music("data/music/remix", 1, 0); + } + relback = 0; + } + update(0, 0); + } + borShutdown(0, DEFAULT_SHUTDOWN_MESSAGE); +} + +#undef GET_ARG +#undef GET_ARG_LEN +#undef GET_ARGP +#undef GET_ARGP_LEN +#undef GET_INT_ARG +#undef GET_FLOAT_ARG +#undef GET_INT_ARGP +#undef GET_FLOAT_ARGP From 5700ed9e28d590c80dabaa886909c7c5a02e7be3 Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 14:58:10 -0400 Subject: [PATCH 2/7] Expand aerial recovery timing diagnostics --- engine/openbor.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/engine/openbor.c b/engine/openbor.c index 683b2852f..9e5eecee8 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -2469,8 +2469,15 @@ void execute_entity_key_script(entity *ent) "AERIAL_RECOVERY_GATE player=%" PRId64 " animation=%" PRIu64 " fall=%" PRIu64 + " mapped_fall=%" PRIu64 " frame=%" PRIu64 " seal=%" PRId64 + " sealtime=%" PRIu64 + " time=%" PRIu64 + " nextanim=%" PRIu64 + " animating=%d" + " falling=%d" + " drop=%d" " dead=%d" " guard=%" PRId64 " maxguard=%d" @@ -2479,8 +2486,15 @@ void execute_entity_key_script(entity *ent) ent->playerindex, (uint64_t)ent->animnum, (uint64_t)ANI_FALL, + (uint64_t)animfalls[ATK_NORMAL], ent->animpos, ent->seal, + ent->sealtime, + _time, + ent->nextanim, + ent->animating, + ent->falling, + ent->drop, (ent->death_state & DEATH_STATE_DEAD) != 0, ent->guardpoints, ent->modeldata.guardpoints, From aca43e57a85d02fd6d2bf55857f0ae49aac8ab82 Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 15:39:46 -0400 Subject: [PATCH 3/7] Trace throw fall animation selection --- engine/openbor.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/engine/openbor.c b/engine/openbor.c index 9e5eecee8..971286423 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -34749,6 +34749,35 @@ int set_fall(entity *ent, entity *other, s_attack *attack, int reset) { int fall = 0; + if (ent + && attack + && ent->playerindex >= 0 + && ent->projectile != BLAST_NONE) + { + printf( + "AERIAL_RECOVERY_FALL player=%" PRId64 + " attack_type=%d" + " selected=%" PRIu64 + " previous=%" PRIu64 + " reset=%d" + " seal=%" PRId64 + " sealtime=%" PRIu64 + " time=%" PRIu64 + " projectile=%d" + " linked=%d\n", + ent->playerindex, + attack->attack_type, + (uint64_t)animfalls[attack->attack_type], + (uint64_t)ent->animnum, + reset, + ent->seal, + ent->sealtime, + _time, + ent->projectile, + ent->link != NULL + ); + } + if ( ent->inbackpain ) fall = animbackfalls[attack->attack_type]; else fall = animfalls[attack->attack_type]; From c57bbab1737c5ddd628ffdea76ff3a78ea8e18ee Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 16:09:32 -0400 Subject: [PATCH 4/7] Trace player fall selection before projectile state --- engine/openbor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 971286423..f3892b7c4 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -34751,8 +34751,7 @@ int set_fall(entity *ent, entity *other, s_attack *attack, int reset) if (ent && attack - && ent->playerindex >= 0 - && ent->projectile != BLAST_NONE) + && ent->playerindex >= 0) { printf( "AERIAL_RECOVERY_FALL player=%" PRId64 From 8825b1b7816a5678f5923ceccc7bfb3c1ffb5fa4 Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 16:59:51 -0400 Subject: [PATCH 5/7] Trace custom aerial recovery script result --- engine/openbor.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index f3892b7c4..af20d38c8 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -2443,6 +2443,9 @@ void execute_ondraw_script(entity *ent) void execute_entity_key_script(entity *ent) { + int recovery_chord; + animation_id_t recovery_animation_before; + int64_t recovery_guard_before; ScriptVariant tempvar; Script *cs ; if(!ent) @@ -2451,6 +2454,14 @@ void execute_entity_key_script(entity *ent) } cs = ent->scripts->key_script; + recovery_chord = ent->playerindex >= 0 + && ent->playerindex < MAX_PLAYERS + && ent->drop + && (player[ent->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) + == (FLAG_MOVEUP | FLAG_JUMP); + recovery_animation_before = ent->animnum; + recovery_guard_before = ent->guardpoints; + /* * Caskey, Damon V. * 2026-08-23 @@ -2459,11 +2470,7 @@ void execute_entity_key_script(entity *ent) * regression. Report the complete native gate state only when * a falling player holds the recovery chord. */ - if(ent->playerindex >= 0 - && ent->playerindex < MAX_PLAYERS - && ent->drop - && (player[ent->playerindex].keys & (FLAG_MOVEUP | FLAG_JUMP)) - == (FLAG_MOVEUP | FLAG_JUMP)) + if(recovery_chord) { printf( "AERIAL_RECOVERY_GATE player=%" PRId64 @@ -2513,6 +2520,29 @@ void execute_entity_key_script(entity *ent) tempvar.lVal = (LONG)ent->playerindex; Script_Set_Local_Variant(cs, "player", &tempvar); Script_Execute(cs); + + if(recovery_chord) + { + printf( + "AERIAL_RECOVERY_RESULT player=%" PRId64 + " animation_before=%" PRIu64 + " animation_after=%" PRIu64 + " guard_before=%" PRId64 + " guard_after=%" PRId64 + " projectile_after=%d" + " falling_after=%d" + " drop_after=%d\n", + ent->playerindex, + (uint64_t)recovery_animation_before, + (uint64_t)ent->animnum, + recovery_guard_before, + ent->guardpoints, + ent->projectile, + ent->falling, + ent->drop + ); + } + //clear to save variant space ScriptVariant_Clear(&tempvar); Script_Set_Local_Variant(cs, "self", &tempvar); From 5e091eb6c921fa5454ee18a8b20796999c946e51 Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 17:15:27 -0400 Subject: [PATCH 6/7] Trace aerial recovery through entity update --- engine/openbor.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/engine/openbor.c b/engine/openbor.c index af20d38c8..6c822917c 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -33925,6 +33925,29 @@ void update_ents() if(ent_list[i]->exists && _time != ent_list[i]->timestamp)// dont update fresh entity { self = ent_list[i]; + bool trace_aerial_recovery = self->playerindex >= 0 + && max_follows >= 9 + && self->animnum == animfollows[8]; + + if(trace_aerial_recovery) + { + printf( + "AERIAL_RECOVERY_UPDATE stage=start" + " player=%" PRId64 + " animation=%" PRIu64 + " falling=%d" + " drop=%d" + " velocity_y=%f" + " takeaction_fall=%d\n", + self->playerindex, + (uint64_t)self->animnum, + self->falling, + self->drop, + self->velocity.y, + self->takeaction == common_fall + ); + } + self->update_mark = UPDATE_MARK_NONE; if(level) { @@ -33943,16 +33966,57 @@ void update_ents() { execute_updateentity_script(self);// execute a script + if(trace_aerial_recovery) + { + printf( + "AERIAL_RECOVERY_UPDATE stage=updateentity" + " animation=%" PRIu64 + " falling=%d drop=%d velocity_y=%f takeaction_fall=%d\n", + (uint64_t)self->animnum, + self->falling, + self->drop, + self->velocity.y, + self->takeaction == common_fall + ); + } if(!self->exists) { continue; } check_ai();// check ai + if(trace_aerial_recovery) + { + printf( + "AERIAL_RECOVERY_UPDATE stage=check_ai" + " animation=%" PRIu64 + " falling=%d drop=%d velocity_y=%f takeaction_fall=%d\n", + (uint64_t)self->animnum, + self->falling, + self->drop, + self->velocity.y, + self->takeaction == common_fall + ); + } if(!self->exists) { continue; } update_animation(); // if not frozen, update animation + if(trace_aerial_recovery) + { + printf( + "AERIAL_RECOVERY_UPDATE stage=animation" + " animation=%" PRIu64 + " frame=%" PRIu64 + " falling=%d drop=%d velocity_y=%f takeaction_fall=%d\n", + (uint64_t)self->animnum, + self->animpos, + self->falling, + self->drop, + self->velocity.y, + self->takeaction == common_fall + ); + } if(!self->exists) { continue; From 291639388850e79e47ce45d634c45da8f972043c Mon Sep 17 00:00:00 2001 From: "Damon V. Caskey" Date: Sun, 23 Aug 2026 19:06:02 -0400 Subject: [PATCH 7/7] Preserve animation ID and reset flag widths --- engine/openbor.c | 29 ++++++++++++++++++----------- engine/openbor.h | 4 +++- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 6c822917c..ba010e9c5 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -12516,9 +12516,10 @@ static int translate_attack_type(char* command, char* filename) } //move here to ease animation name to id logic -static int translate_ani_id(const char *value, s_model *newchar, s_anim *newanim) +static animation_id_t translate_ani_id(const char *value, s_model *newchar, s_anim *newanim) { - int ani_id = -1, tempInt; + animation_id_t ani_id = ANIMATION_ID_INVALID; + int tempInt; //those are dummy values to simplify code static s_model mdl; static s_anim ani; @@ -15772,8 +15773,8 @@ s_model *load_cached_model(char *name, char *owner, char unload) ArgList arglist; - int ani_id = ANI_NONE; - int script_id = -1; + animation_id_t ani_id = ANI_NONE; + animation_id_t script_id = ANIMATION_ID_INVALID; int frm_id = -1; bool at_cmd_mergeable = false; int i = 0; @@ -17822,7 +17823,7 @@ s_model *load_cached_model(char *name, char *owner, char unload) ani_id = translate_ani_id(value, newchar, newanim); } - if(ani_id < 0) + if(ani_id == ANIMATION_ID_INVALID) { shutdownmessage = "Invalid animation name!"; goto lCleanup; @@ -17850,6 +17851,8 @@ s_model *load_cached_model(char *name, char *owner, char unload) int freespecial_number; + animation_id_t sync_animation_id; + s_command_token animation_name_token; const char* freespecial_error; @@ -17876,9 +17879,13 @@ s_model *load_cached_model(char *name, char *owner, char unload) * "none" intentionally translates to FRAME_NONE and * continues to disable the default synchronization. */ - newanim->sync = freespecial_recognized + sync_animation_id = freespecial_recognized ? animspecials[freespecial_number - 1] : translate_ani_id(value, NULL, NULL); + + newanim->sync = sync_animation_id == ANIMATION_ID_INVALID + ? FRAME_NONE + : (int64_t)sync_animation_id; } break; case CMD_MODEL_DELAY: @@ -18422,7 +18429,7 @@ s_model *load_cached_model(char *name, char *owner, char unload) /* * Cancel belongs to the currently active animation. */ - if(!newanim || ani_id < 0) { + if(!newanim || ani_id == ANIMATION_ID_INVALID) { shutdownmessage = "Cannot add cancel command: animation not specified.\n"; @@ -20760,7 +20767,7 @@ s_model *load_cached_model(char *name, char *owner, char unload) } scriptbuf[scriptlen - strclen(sur_text)] = 0; // cut last chars scriptlen = strlen(scriptbuf); - if(ani_id >= 0) + if(ani_id != ANIMATION_ID_INVALID) { if(script_id != ani_id) // if expression 1 { @@ -20785,7 +20792,7 @@ s_model *load_cached_model(char *name, char *owner, char unload) buffer_append(&scriptbuf, buf + pos - len, len, &sbsize, &scriptlen); pos += strclen("@end_script"); - if(ani_id >= 0) + if(ani_id != ANIMATION_ID_INVALID) { buffer_append(&scriptbuf, endifid_text, 0xffffff, &sbsize, &scriptlen);// put back last chars } @@ -20799,7 +20806,7 @@ s_model *load_cached_model(char *name, char *owner, char unload) bool first_command_argument; //translate @cmd into script function call - if(ani_id < 0) + if(ani_id == ANIMATION_ID_INVALID) { shutdownmessage = "command '@cmd' must follow an animation!"; goto lCleanup; @@ -28291,7 +28298,7 @@ void update_frame(entity *ent, uint64_t f) } -void ent_set_anim(entity *acting_entity, animation_id_t aninum, bool resetable) { +void ent_set_anim(entity *acting_entity, animation_id_t aninum, int resetable) { s_anim *ani = NULL; int animpos; diff --git a/engine/openbor.h b/engine/openbor.h index 06159b03d..e48702795 100644 --- a/engine/openbor.h +++ b/engine/openbor.h @@ -1001,6 +1001,8 @@ typedef enum e_animations //Animations typedef uint64_t animation_id_t; +#define ANIMATION_ID_INVALID UINT64_MAX + typedef enum { BOSS_SLOW_OFF, @@ -4808,7 +4810,7 @@ void biker_drive(void); void ent_default_init(entity *e); void ent_spawn_ent(entity *ent); void ent_summon_ent(entity *ent); -void ent_set_anim(entity *ent, animation_id_t aninum, bool resetable); +void ent_set_anim(entity *ent, animation_id_t aninum, int resetable); void ent_set_colourmap(entity *ent, uint64_t which); void ent_set_model(entity *ent, char *modelname, int syncAnim); entity *spawn_attack_flash(entity *ent, s_attack *attack, int attack_flash, int model_flash);