├── server ├── modules │ ├── game │ │ └── const.lua │ ├── utils.lua │ └── user_codes.lua └── docker-compose.yml ├── .defignore ├── game ├── system │ ├── annotations_manual.lua │ ├── game.render │ ├── game.display_profiles │ ├── world_gui.material │ ├── camera.go │ └── game.render_script ├── game.lua ├── db.lua ├── profile │ ├── Tutorial.lua │ ├── Profile.lua │ ├── platform_adapters │ │ ├── platform_ios.lua │ │ └── platform_web.lua │ └── Platform.lua ├── const.lua ├── analytics │ ├── Analytics.lua │ └── analytics_gameanalytics.lua ├── server │ ├── Server.lua │ └── ServerRequester.lua └── settings │ └── window_settings.lua ├── README.md ├── resources ├── json │ ├── ads.json │ ├── quests.json │ ├── constants.json │ ├── promocodes.json │ ├── token_lots.json │ ├── token_order.json │ ├── token_config.json │ ├── token_groups.json │ ├── iaps_ios.json │ └── iaps_android.json ├── gamedata.proto ├── eva_settings_mobile.json ├── game.proto ├── locale │ ├── en.json │ └── ru.json └── evadata.proto ├── utils ├── build_dev_ios.sh ├── export_data.sh ├── server_start.sh ├── upload_to_web.sh └── update_annotations.sh ├── eva ├── system │ ├── eva.render │ └── eva.texture_profiles ├── modules │ ├── random.lua │ ├── cache.lua │ ├── resources.lua │ ├── rate │ │ ├── rate_stub.lua │ │ ├── rate_mobile.lua │ │ └── rate_yandex.lua │ ├── render │ │ └── effects │ │ │ ├── default │ │ │ ├── default.vp │ │ │ └── default.material │ │ │ └── render.go │ ├── status.lua │ ├── render.lua │ ├── social │ │ ├── stub.lua │ │ └── gpgs.lua │ ├── share.lua │ ├── iaps │ │ ├── iaps_mobile.lua │ │ └── iaps_yandex.lua │ ├── ads │ │ ├── ads_adapter_interface.lua │ │ └── ads_yandex.lua │ ├── social.lua │ ├── gdpr.lua │ ├── feature.lua │ ├── rating.lua │ ├── migrations.lua │ ├── vibrate.lua │ ├── storage.lua │ ├── input │ │ └── input_pinch.lua │ ├── queue.lua │ ├── callbacks.lua │ ├── errors.lua │ ├── camera │ │ ├── camera_pinch.lua │ │ └── camera_drag.lua │ ├── db.lua │ ├── pathfinder.lua │ ├── rate.lua │ ├── events.lua │ ├── proto.lua │ ├── promocode.lua │ ├── labels.lua │ └── hexgrid │ │ └── hexgrid_convertations.lua ├── libs │ ├── astar │ │ ├── path.lua │ │ ├── node.lua │ │ └── basic_handler.lua │ ├── smart │ │ ├── smart.lua │ │ └── value.lua │ ├── b64.lua │ ├── sitelock.lua │ └── sequence.lua ├── resources │ ├── promocode_settings_example.lua │ ├── festivals_settings_example.lua │ ├── trucks_settins_example.lua │ ├── quests_settings_example.lua │ ├── window_settings_example.lua │ └── evadata.proto ├── scripts │ ├── grid_handler.lua │ ├── hexgrid_handler.lua │ └── isogrid_handler.lua ├── materials │ ├── greyscale_gui │ │ ├── greyscale_gui.vp │ │ ├── greyscale_gui.material │ │ └── greyscale_gui.fp │ └── sprite.material └── app.lua ├── input └── game.input_binding ├── assets ├── fonts │ ├── Marvin.otf │ ├── troika.otf │ ├── MontserratAlternates-Black.otf │ └── game.font ├── sounds │ ├── sfx_click.wav │ └── music_game.ogg ├── images │ ├── empty.png │ ├── pixel.png │ ├── scene_menu │ │ ├── icon_no_wifi.png │ │ └── background_menu.png │ ├── window_general │ │ ├── button_blue.png │ │ ├── button_close.png │ │ ├── button_green.png │ │ ├── button_grey.png │ │ ├── button_purple.png │ │ ├── button_red.png │ │ ├── button_yellow.png │ │ └── background_window.png │ └── window_settings │ │ ├── i_music_off.png │ │ ├── i_music_on.png │ │ ├── i_notif_off.png │ │ ├── i_notif_on.png │ │ ├── i_sound_off.png │ │ ├── i_sound_on.png │ │ ├── i_vibro_off.png │ │ └── i_vibro_on.png ├── system │ └── custom.display_profiles ├── atlases │ ├── scene_menu.atlas │ ├── window_settings.atlas │ └── window_general.atlas └── scenes │ ├── scene_menu.collection │ └── scene_game.collection ├── settings ├── ios.ini ├── android.ini └── yandex.ini ├── bundle ├── icons │ ├── icon_114.png │ ├── icon_120.png │ ├── icon_144.png │ ├── icon_152.png │ ├── icon_167.png │ ├── icon_180.png │ ├── icon_192.png │ ├── icon_36.png │ ├── icon_48.png │ ├── icon_57.png │ ├── icon_72.png │ ├── icon_76.png │ └── icon_96.png ├── launch_images │ ├── ios_launch_320x480.png │ ├── ios_launch_640x960.png │ ├── ios_launch_1024x768.png │ ├── ios_launch_1125x2436.png │ ├── ios_launch_1242x2208.png │ ├── ios_launch_1334x750.png │ ├── ios_launch_1536x2048.png │ ├── ios_launch_1668x2224.png │ ├── ios_launch_2048x1536.png │ ├── ios_launch_2048x2732.png │ ├── ios_launch_2208x1242.png │ ├── ios_launch_2224x1668.png │ ├── ios_launch_2436x1125.png │ ├── ios_launch_2732x2048.png │ ├── ios_launch_640x1136.png │ ├── ios_launch_750x1334.png │ └── ios_launch_768x1024.png └── manifests │ └── game.appmanifest ├── gui ├── ui_logo_insality_games │ ├── images │ │ ├── Games.png │ │ ├── Heart.png │ │ └── Insality.png │ ├── insality_games.script │ ├── insality_games.go │ ├── insality_games.atlas │ └── insality_games.gui_script ├── window_info │ ├── window_info.collection │ └── window_info.gui_script ├── window_admin │ └── window_admin.collection ├── window_confirm │ ├── window_confirm.collection │ └── window_confirm.gui_script ├── window_settings │ └── window_settings.collection ├── window_template │ ├── window_template.collection │ └── window_template.gui_script ├── ui_scene_transition │ ├── ui_scene_transition.gui_script │ └── ui_scene_transition.lua ├── components │ ├── window_shadow.gui │ ├── button_close.gui │ └── button_text.gui ├── scene_game │ └── scene_game.gui_script └── scene_menu │ └── scene_menu.gui_script ├── .gitattributes ├── .gitignore ├── export_config ├── rules_locale.json ├── config.json └── custom_handlers.js ├── game.code-workspace ├── LICENSE ├── .luacheckrc ├── deployer_build_stats.csv └── settings_deployer /server/modules/game/const.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.defignore: -------------------------------------------------------------------------------- 1 | /server 2 | /dist 3 | /build 4 | -------------------------------------------------------------------------------- /game/system/annotations_manual.lua: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Defold-template 2 | 3 | --- 4 | -------------------------------------------------------------------------------- /resources/json/ads.json: -------------------------------------------------------------------------------- 1 | { 2 | "ads": {} 3 | } -------------------------------------------------------------------------------- /game/game.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | return M 5 | -------------------------------------------------------------------------------- /resources/json/quests.json: -------------------------------------------------------------------------------- 1 | { 2 | "quests": {} 3 | } -------------------------------------------------------------------------------- /resources/json/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "constants": {} 3 | } -------------------------------------------------------------------------------- /resources/json/promocodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "promocodes": {} 3 | } -------------------------------------------------------------------------------- /resources/json/token_lots.json: -------------------------------------------------------------------------------- 1 | { 2 | "token_lots": {} 3 | } -------------------------------------------------------------------------------- /utils/build_dev_ios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | deployer ibd 4 | -------------------------------------------------------------------------------- /eva/system/eva.render: -------------------------------------------------------------------------------- 1 | script: "/eva/system/eva.render_script" 2 | -------------------------------------------------------------------------------- /utils/export_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sheets-exporter export 4 | -------------------------------------------------------------------------------- /game/system/game.render: -------------------------------------------------------------------------------- 1 | script: "/game/system/game.render_script" 2 | -------------------------------------------------------------------------------- /resources/json/token_order.json: -------------------------------------------------------------------------------- 1 | { 2 | "token_order": [ 3 | "no_ads" 4 | ] 5 | } -------------------------------------------------------------------------------- /input/game.input_binding: -------------------------------------------------------------------------------- 1 | mouse_trigger { 2 | input: MOUSE_BUTTON_1 3 | action: "touch" 4 | } 5 | -------------------------------------------------------------------------------- /resources/gamedata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "evadata.proto"; 3 | 4 | package gamedata; 5 | -------------------------------------------------------------------------------- /assets/fonts/Marvin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insality/defold-template/HEAD/assets/fonts/Marvin.otf -------------------------------------------------------------------------------- /assets/fonts/troika.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insality/defold-template/HEAD/assets/fonts/troika.otf -------------------------------------------------------------------------------- /assets/sounds/sfx_click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insality/defold-template/HEAD/assets/sounds/sfx_click.wav -------------------------------------------------------------------------------- /utils/server_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose -f ./server/docker-compose.yml up -d --remove-orphans 4 | -------------------------------------------------------------------------------- /settings/ios.ini: -------------------------------------------------------------------------------- 1 | [project] 2 | platform_name = ios 3 | 4 | [eva] 5 | settings_override = /resources/eva_settings_mobile.json 6 | -------------------------------------------------------------------------------- /settings/android.ini: -------------------------------------------------------------------------------- 1 | [project] 2 | platform_name = android 3 | 4 | [eva] 5 | settings_override = /resources/eva_settings_mobile.json 6 | -------------------------------------------------------------------------------- /settings/yandex.ini: -------------------------------------------------------------------------------- 1 | [project] 2 | platform_name = yandex 3 | 4 | [eva] 5 | settings_override = /resources/eva_settings_yandex.json 6 | -------------------------------------------------------------------------------- /assets/fonts/MontserratAlternates-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insality/defold-template/HEAD/assets/fonts/MontserratAlternates-Black.otf -------------------------------------------------------------------------------- /game/system/game.display_profiles: -------------------------------------------------------------------------------- 1 | profiles { 2 | name: "Landscape" 3 | qualifiers { 4 | width: 1600 5 | height: 900 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /assets/images/empty.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f81859279fbf885958d1e293d67976d81751e7d836c81c77f02e31ca5dd8a359 3 | size 2803 4 | -------------------------------------------------------------------------------- /assets/images/pixel.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:15c27afe47c3db144dcd51e62bf45ee467b933a44847ffb8298d731b2b8fab54 3 | size 14971 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_114.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:25025af3a154f979db632b63ab967aea443d01c8ab75065f45dee28900cb322d 3 | size 2936 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_120.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3cac80c05dacda932c73672db068ddbd9b32edf6158fdb588820ddeeac6541d6 3 | size 3047 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_144.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9f3c95af9d7c32cf8ddb92b65f3c2c305e9a6baaf49d9923ac6e6aaaa44ebfca 3 | size 3464 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_152.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0a2504d9984bacd75137a259f6faaed15ef7c9c000c8b7c6bef006a96d6cb20d 3 | size 3619 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_167.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6e08b1d35b7c46763658acb1faae1314e930343bbdbf06641a13d8d8b72554a2 3 | size 4458 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_180.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:19516e55986f333fc0e2137d5bfb4cefe8ccba3d8d540f6e53cd80730c0c1be8 3 | size 4108 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_192.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f5eb242d233096cc41d59ad54efae3aebcf0351ac9f49dbf909193158476e767 3 | size 4348 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_36.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a86e83c1978106e3b59ae594bb91e3b45024590f0614ba842fbdce88b8964ae9 3 | size 1557 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_48.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b25c06f3fab6a4d441603aa14de18d29c5a44d6cae808a559cfdf16f6404b52 3 | size 1821 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_57.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1c96b14f94c11e4fb0091d43e409e0c93debcd0176e38ca345eb0391ed8bf172 3 | size 1937 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_72.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:493e31f0715ebcba528e6456fb4786895b656fc12d4e81525a12603a2d20ee5b 3 | size 2226 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_76.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b99f8befc8d1b07a84858376f4e3e0bd0e55d5ac0244d9ea543564a7f6c6f349 3 | size 2240 4 | -------------------------------------------------------------------------------- /bundle/icons/icon_96.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:46208376b145a613a23e1abe1e97fd26739946d5a322a6c588f6f3d34a259448 3 | size 2667 4 | -------------------------------------------------------------------------------- /eva/modules/random.lua: -------------------------------------------------------------------------------- 1 | --- Eva random module 2 | -- Can create seeded generator 3 | -- @submodule eva 4 | 5 | 6 | local M = {} 7 | 8 | 9 | return M 10 | -------------------------------------------------------------------------------- /assets/sounds/music_game.ogg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c45ffd2e2b1657be0a2c8d7e31f5b778cfb8b4280387cdf43174a32e1695e66a 3 | size 368135 4 | -------------------------------------------------------------------------------- /assets/images/scene_menu/icon_no_wifi.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f7e271fad65912cee7da530c33bb171bd28be75bb1e21f5c1d966bbad0696d95 3 | size 4086 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_320x480.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:efd10c766c22284d9a28599dde8038fd802e8db5d20f8269d40506de8b657418 3 | size 114 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_640x960.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:acc1256583d570fc98e5f650dfd28b32e948af6cd8d93c6aa465d4f6a434cf19 3 | size 183 4 | -------------------------------------------------------------------------------- /assets/images/scene_menu/background_menu.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d57228b27a10a4aadffc4c658b74b7cb14a09003f48dd4a1c02ccbd96ff798d9 3 | size 36835 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_blue.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:29da393a5056536651427f8245b1be857c81a16957be5bddf40db4de98750f2f 3 | size 1888 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_close.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1685b95122e96431d205a3f7085ca1e13d00c0a55c2b61e103f23fc5ee52d495 3 | size 688 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_green.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6ccf45c5a7495d9b0ad77217e288bdf36814b717524e5faa70043c0979d2d142 3 | size 1986 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_grey.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0e15efddaea7698785decc17f4ae485a512c381b5cccb615e1fefb3a487d4f62 3 | size 1808 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_purple.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0f4ae4f005e92ed3f4fa08c064d78aa978adc37185ce75d14ea04f2e4c1e9c20 3 | size 1841 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_red.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c2709db7e1006bdccb8fbe60b9ed50be7c2ca3315cada7e3f9a9f83602625b86 3 | size 1815 4 | -------------------------------------------------------------------------------- /assets/images/window_general/button_yellow.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:197408beb1bb5a2a3f1265c804089b249805b3be44b960ec5fe4d9b25a8ed55a 3 | size 1956 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_music_off.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1ea48e56d2f86d3b278669d22c891e5db9a195da0b1d6e61cfe4f96e25c75249 3 | size 1233 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_music_on.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:88aef232863e057c1a786c2f7f8af84adc2b43e463d45682301f90c3da8324ec 3 | size 947 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_notif_off.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c56459f0ca6abde7bc6078dcadf08eca81e3e525abff2d25743a22f8d59ca73a 3 | size 1054 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_notif_on.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5024e328751b0e71ee4857a593d158c068e27ae66b334b1129a22bdfd54dbf7c 3 | size 820 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_sound_off.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c8613f506f3af87acf03fb6c0d4dde165943964687b39d5e459a74ee39ab2afe 3 | size 1061 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_sound_on.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6ca74734a38b06d38119a404d3fe37f9cf83eba218907b6dbba16eca799821c9 3 | size 1488 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_vibro_off.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d2f9dc7935cdef9a8d3c9414276481a5f33f62f4c3fd1e43d867d1ed85b86d27 3 | size 1224 4 | -------------------------------------------------------------------------------- /assets/images/window_settings/i_vibro_on.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1216e8230efe2eada525deaf5249eadb403197c16c416e7a796077c8d031b905 3 | size 1186 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1024x768.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a5b114e58c9ba1ad4f96869fea5c442e5334a958ad68e3b0a4092ecf41c7bd90 3 | size 202 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1125x2436.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:437202b53d2085f609c4c2d38912d84b38ff57a0868362923b03ea268380551a 3 | size 492 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1242x2208.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4dc6ca443cbc2a7476e5dc9b199e5a5dba987bc8baff83ba0f67b3419a128099 3 | size 493 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1334x750.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9443595d749fc23de8af50adaea2e56aa494992301dfa640439717c3b633bbaa 3 | size 242 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1536x2048.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f51707d0b8499bba6c8f9f30ce273d8664bdaab54684975c196084788cf94d4d 3 | size 552 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_1668x2224.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fcd1489008b4ec75cfdbdc00627ae0d07242d908c1d5f381cd83cee1470e479e 3 | size 70887 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2048x1536.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:939cf88119ad43f21da2fc921ba5b002a2ea01ae961905825e6b72f37828679c 3 | size 552 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2048x2732.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0d155e8a1e98ccfe1067727af7d83914c267143959236f440a4e78c2291d4e13 3 | size 913 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2208x1242.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6d2c3e017c963e7dbfe17ac38c77cd4c8d50d2138a391e61a850c521310480bf 3 | size 491 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2224x1668.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fbca6663ae63a4eb358a12f83bb1dc5959de6ed9016291fc9dc8d6c176980486 3 | size 69780 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2436x1125.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:505363771b9af5336b135b85a4a483fdb88fd7183117b2ab0f0d1bcc957be7b6 3 | size 491 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_2732x2048.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6b03a18792c85e2ca62dd6d732a22d134bc66eedd00968449e4576cf5063f884 3 | size 913 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_640x1136.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8aeb1ac35aa2be845e9b84a5bbc5ac4447abd9dba2a203a5a716a2b077ed9909 3 | size 196 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_750x1334.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c2a9be80ec8a5793c4794baba08171716e90f7b8653e37e8f02e0c64952d2fe6 3 | size 242 4 | -------------------------------------------------------------------------------- /bundle/launch_images/ios_launch_768x1024.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:814032f8e6d9b7a5949e72a229c82e7f1420298df62f826a7485db4d1f1937ab 3 | size 202 4 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/images/Games.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7720ab643699e80e8826920233b6c6dede7397d7d9c36d7bf19084614c3a39db 3 | size 12444 4 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/images/Heart.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3810f395dbaef3c31d1ab5197aeddf32b73023f00335f58166c10742b49d3c1d 3 | size 5627 4 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/images/Insality.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6b5123d0cef6742822208959a3c227e1ff200399a25c491180c60a760055575e 3 | size 46435 4 | -------------------------------------------------------------------------------- /assets/images/window_general/background_window.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:02c9d92d0e185faf4fa0309996258137be6eb16f289a1367f9c5657d861f86d5 3 | size 6357 4 | -------------------------------------------------------------------------------- /utils/upload_to_web.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Start upload" 4 | ssh root@insality.com "rm -r /home/files/seabattle" 5 | scp -r $1 root@insality.com:/home/files/seabattle 6 | echo "End upload" 7 | -------------------------------------------------------------------------------- /eva/modules/cache.lua: -------------------------------------------------------------------------------- 1 | --- Eva cache module. 2 | -- It can store and reuse some files from 3 | -- internet or other data. 4 | -- @submodule eva 5 | 6 | 7 | local M = {} 8 | 9 | 10 | return M 11 | -------------------------------------------------------------------------------- /resources/json/token_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "token_config": { 3 | "no_ads": { 4 | "category": "ads", 5 | "default": 0, 6 | "max": 1, 7 | "min": 0, 8 | "name": "no_ads" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /resources/json/token_groups.json: -------------------------------------------------------------------------------- 1 | { 2 | "token_groups": { 3 | "no_ads": { 4 | "tokens": [ 5 | { 6 | "amount": 1, 7 | "token_id": "no_ads" 8 | } 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /eva/libs/astar/path.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | function M.get(nodes, total_cost, total_score) 5 | return { 6 | nodes = nodes, 7 | total_cost = total_cost, 8 | total_score = total_score 9 | } 10 | end 11 | 12 | 13 | return M 14 | -------------------------------------------------------------------------------- /eva/modules/resources.lua: -------------------------------------------------------------------------------- 1 | --- Eva module. Use for work with bundle resources. 2 | -- It can async load resources (for example images from 3 | -- bundled resource) 4 | -- @submodule eva 5 | 6 | 7 | local M = {} 8 | 9 | 10 | return M 11 | -------------------------------------------------------------------------------- /eva/resources/promocode_settings_example.lua: -------------------------------------------------------------------------------- 1 | -- Promocode settings examples 2 | local M = {} 3 | 4 | 5 | function M.on_code_redeem(code) 6 | end 7 | 8 | 9 | function M.is_can_code_redeem(code) 10 | return true 11 | end 12 | 13 | 14 | return M 15 | -------------------------------------------------------------------------------- /resources/json/iaps_ios.json: -------------------------------------------------------------------------------- 1 | { 2 | "iaps": { 3 | "no_ads": { 4 | "category": "ads", 5 | "forever": true, 6 | "ident": "free.insality.noads", 7 | "price": 1.99, 8 | "token_group_id": "no_ads" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /resources/json/iaps_android.json: -------------------------------------------------------------------------------- 1 | { 2 | "iaps": { 3 | "no_ads": { 4 | "category": "ads", 5 | "forever": true, 6 | "ident": "free.insality.noads", 7 | "price": 1.99, 8 | "token_group_id": "no_ads" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | *.ogg filter=lfs diff=lfs merge=lfs -text 3 | *.ttf filter=lfs diff=lfs merge=lfs -text 4 | *.jpg filter=lfs diff=lfs merge=lfs -text 5 | *.jpeg filter=lfs diff=lfs merge=lfs -text 6 | *.zip filter=lfs diff=lfs merge=lfs -text 7 | -------------------------------------------------------------------------------- /assets/system/custom.display_profiles: -------------------------------------------------------------------------------- 1 | 2 | profiles 3 | { 4 | name: "Landscape" 5 | qualifiers { 6 | width: 1280 7 | height: 720 8 | } 9 | } 10 | profiles 11 | { 12 | name: "Portrait" 13 | qualifiers { 14 | width: 720 15 | height: 1280 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /utils/update_annotations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bash ~/code/lua/emmylua-protoc-annotations/export.sh \ 4 | ~/code/defold/sea-battle-universe/resources/ \ 5 | ~/code/defold/sea-battle-universe/resources/server.proto > ~/code/defold/sea-battle-universe/annotations/annotations-game.lua 6 | -------------------------------------------------------------------------------- /game/system/world_gui.material: -------------------------------------------------------------------------------- 1 | name: "gui" 2 | tags: "world_gui" 3 | vertex_program: "/builtins/materials/gui.vp" 4 | fragment_program: "/builtins/materials/gui.fp" 5 | vertex_space: VERTEX_SPACE_WORLD 6 | vertex_constants { 7 | name: "view_proj" 8 | type: CONSTANT_TYPE_VIEWPROJ 9 | } 10 | -------------------------------------------------------------------------------- /game/db.lua: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | 3 | local M = {} 4 | 5 | 6 | ---@param contants_id string 7 | ---@return string|number|boolean 8 | function M.get_constant(contants_id) 9 | return eva.db.get("Constants").constants[contants_id] 10 | end 11 | 12 | 13 | return M 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.internal 2 | /build 3 | .externalToolBuilders 4 | .DS_Store 5 | Thumbs.db 6 | .lock-wscript 7 | *.pyc 8 | .project 9 | .cproject 10 | builtins 11 | deployer_version_settings.txt 12 | provisions 13 | dist 14 | /server/.cookie 15 | manifest.private.der 16 | manifest.public.der 17 | 18 | .cache_deployer 19 | -------------------------------------------------------------------------------- /eva/modules/rate/rate_stub.lua: -------------------------------------------------------------------------------- 1 | local Rate = {} 2 | 3 | 4 | function Rate.is_supported() 5 | return false 6 | end 7 | 8 | 9 | function Rate.is_can_show(callback) 10 | assert(callback) 11 | callback(true) 12 | end 13 | 14 | 15 | function Rate.request_review() 16 | return false 17 | end 18 | 19 | 20 | return Rate 21 | -------------------------------------------------------------------------------- /eva/resources/festivals_settings_example.lua: -------------------------------------------------------------------------------- 1 | -- Festivals example 2 | local M = {} 3 | 4 | 5 | function M.is_can_start(festival_id, festival) 6 | return true 7 | end 8 | 9 | 10 | function M.on_festival_start(festival_id, festival) 11 | 12 | end 13 | 14 | 15 | function M.on_festival_end(festival_id, festival) 16 | 17 | end 18 | 19 | 20 | return M 21 | -------------------------------------------------------------------------------- /eva/modules/render/effects/default/default.vp: -------------------------------------------------------------------------------- 1 | uniform highp mat4 view_proj; 2 | 3 | // positions are in world space 4 | attribute highp vec4 position; 5 | attribute mediump vec2 texcoord0; 6 | 7 | varying mediump vec2 var_texcoord0; 8 | 9 | void main() 10 | { 11 | gl_Position = view_proj * vec4(position.xyz, 1.0); 12 | var_texcoord0 = texcoord0; 13 | } 14 | -------------------------------------------------------------------------------- /assets/atlases/scene_menu.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/images/scene_menu/background_menu.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/images/scene_menu/icon_no_wifi.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | margin: 0 10 | extrude_borders: 2 11 | inner_padding: 0 12 | max_page_width: 0 13 | max_page_height: 0 14 | -------------------------------------------------------------------------------- /resources/eva_settings_mobile.json: -------------------------------------------------------------------------------- 1 | { 2 | "ads": { 3 | "adapter": "unity" 4 | }, 5 | "log": { 6 | "level": "WARN" 7 | }, 8 | "rate": { 9 | "adapter": "mobile" 10 | }, 11 | "server": { 12 | "server_host": "some_url", 13 | "server_port": 443, 14 | "debug_log": false, 15 | "use_ssl": true 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /export_config/rules_locale.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "locales": { 4 | "lists": ["Locales"], 5 | "handlers": [ 6 | { 7 | "type": "add_id_by_values", 8 | "config": { 9 | "ids": ["lang_name"] 10 | } 11 | } 12 | ], 13 | "save_param": { 14 | "separate_fields": [ 15 | "ru", "en" 16 | ], 17 | "no_beautify": false 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /game.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.exclude": { 9 | ".cache_deployer": true, 10 | "dist": true, 11 | "build": true, 12 | ".internal": true, 13 | ".gitattributes": true, 14 | "manifest.private.der": true, 15 | "manifest.public.der": true, 16 | "provisions": true, 17 | "input": true, 18 | "bundle": true, 19 | "assets": true, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eva/libs/astar/node.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | function M.get(x, y, move_cost, parent) 5 | return { 6 | x = x, -- Where is the node located 7 | y = y, -- Where is the node located 8 | move_cost = move_cost, -- Total move cost to reach this node. Nil if node is unreachable 9 | parent = parent, -- Parent node 10 | score = 0, -- Calculated score for this node 11 | uid = x*10000+y -- set the location id - unique for each location in the map 12 | } 13 | end 14 | 15 | 16 | return M 17 | -------------------------------------------------------------------------------- /eva/modules/render/effects/render.go: -------------------------------------------------------------------------------- 1 | embedded_components { 2 | id: "default" 3 | type: "model" 4 | data: "mesh: \"/eva/system/quad.dae\"\n" 5 | "material: \"/eva/modules/render/effects/default/default.material\"\n" 6 | "skeleton: \"\"\n" 7 | "animations: \"\"\n" 8 | "default_animation: \"\"\n" 9 | "name: \"unnamed\"\n" 10 | "" 11 | position { 12 | x: 0.0 13 | y: 0.0 14 | z: 0.0 15 | } 16 | rotation { 17 | x: 0.0 18 | y: 0.0 19 | z: 0.0 20 | w: 1.0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eva/libs/smart/smart.lua: -------------------------------------------------------------------------------- 1 | local value = require("eva.libs.smart.value") 2 | 3 | local M = {} 4 | 5 | 6 | --- Params is rules for value 7 | -- default - default value, 0 by default 8 | -- name - name of value 9 | -- min 10 | -- max 11 | function M.new(params, data_table) 12 | data_table = data_table or { 13 | amount = nil, 14 | offset = 0, 15 | total_sum = 0, 16 | } 17 | 18 | local v = setmetatable({}, { __index = value }) 19 | v:init(params, data_table) 20 | 21 | return v 22 | end 23 | 24 | 25 | return M 26 | -------------------------------------------------------------------------------- /game/profile/Tutorial.lua: -------------------------------------------------------------------------------- 1 | local class = require("eva.libs.middleclass") 2 | 3 | ---@class Tutorial 4 | local Tutorial = class("game.Tutorial") 5 | 6 | 7 | ---@param data game.TutorialData 8 | function Tutorial:initialize(data) 9 | self._data = data 10 | end 11 | 12 | 13 | ---@param is_enabled boolean 14 | function Tutorial:set_enabled(is_enabled) 15 | self._data.is_enabled = is_enabled 16 | end 17 | 18 | 19 | function Tutorial:is_enabled() 20 | return self._data.is_enabled 21 | end 22 | 23 | 24 | return Tutorial 25 | -------------------------------------------------------------------------------- /eva/resources/trucks_settins_example.lua: -------------------------------------------------------------------------------- 1 | --@local 2 | local M = {} 3 | 4 | 5 | function M.get_truck_lifetime(truck_id, truck) 6 | 7 | end 8 | 9 | 10 | function M.is_can_arrive(truck_id, truck) 11 | 12 | end 13 | 14 | 15 | function M.on_truck_arrive(truck_id, truck) 16 | 17 | end 18 | 19 | 20 | function M.get_truck_cooldown(truck_id, truck) 21 | 22 | end 23 | 24 | 25 | function M.is_can_leave(truck_id, truck) 26 | 27 | end 28 | 29 | 30 | function M.on_truck_leave(truck_id, truck) 31 | 32 | end 33 | 34 | 35 | return M 36 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/insality_games.script: -------------------------------------------------------------------------------- 1 | local const = require("game.const") 2 | 3 | go.property("notify_url", msg.url()) 4 | 5 | 6 | local function finish(self) 7 | go.delete(".") 8 | msg.post(self.notify_url, const.MSG.LOADER_LOGO_SHOWED) 9 | end 10 | 11 | 12 | function init(self) 13 | if sys.get_engine_info().is_debug then 14 | finish(self) 15 | end 16 | end 17 | 18 | 19 | function on_message(self, message_id, message, sender) 20 | if message_id == const.MSG.DESTROY_GO then 21 | finish(self) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /eva/modules/rate/rate_mobile.lua: -------------------------------------------------------------------------------- 1 | local device = require("eva.modules.device") 2 | 3 | local Rate = {} 4 | 5 | 6 | function Rate.is_supported() 7 | return review and review.is_supported() 8 | end 9 | 10 | 11 | function Rate.is_can_show(callback) 12 | assert(callback) 13 | 14 | if not device.is_mobile() then 15 | callback(false) 16 | end 17 | 18 | return callback(true) 19 | end 20 | 21 | 22 | function Rate.request_review() 23 | if not review then 24 | return 25 | end 26 | 27 | review.request_review() 28 | end 29 | 30 | 31 | return Rate 32 | -------------------------------------------------------------------------------- /server/modules/utils.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | function M.length(t) 5 | local length = 0 6 | for _ in pairs(t) do 7 | length = length + 1 8 | end 9 | return length 10 | end 11 | 12 | 13 | function M.deepcopy(orig) 14 | local copy 15 | if type(orig) == "table" then 16 | copy = {} 17 | for orig_key, orig_value in next, orig, nil do 18 | copy[M.deepcopy(orig_key)] = M.deepcopy(orig_value) 19 | end 20 | setmetatable(copy, M.deepcopy(getmetatable(orig))) 21 | else 22 | copy = orig 23 | end 24 | return copy 25 | end 26 | 27 | 28 | return M 29 | -------------------------------------------------------------------------------- /eva/scripts/grid_handler.lua: -------------------------------------------------------------------------------- 1 | -- Handler for usual grid astar 2 | 3 | local const = require("eva.const") 4 | local basic_handler = require("eva.libs.astar.basic_handler") 5 | 6 | local M = {} 7 | 8 | 9 | function M.new_handler(get_node_fn, options) 10 | local data = basic_handler.get_handler(get_node_fn) 11 | 12 | data.neighbors = const.ASTAR.GRID.NEIGHBORS 13 | if options and options.diagonal then 14 | data.neighbors = const.ASTAR.GRID.NEIGHBORS_DIAGONAL 15 | end 16 | 17 | return setmetatable(data, { __index = basic_handler }) 18 | end 19 | 20 | 21 | return M 22 | -------------------------------------------------------------------------------- /eva/materials/greyscale_gui/greyscale_gui.vp: -------------------------------------------------------------------------------- 1 | uniform mediump mat4 view_proj; 2 | 3 | // positions are in world space 4 | attribute mediump vec4 position; 5 | attribute mediump vec2 texcoord0; 6 | attribute lowp vec4 color; 7 | 8 | varying mediump vec2 var_texcoord0; 9 | varying lowp vec4 var_color; 10 | varying lowp float var_saturate_adjust; 11 | 12 | void main() 13 | { 14 | var_saturate_adjust = 1.0 - position.z; 15 | var_texcoord0 = texcoord0; 16 | var_color = vec4(color.rgb * color.a, color.a); 17 | gl_Position = view_proj * vec4(position.xy, 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /eva/modules/status.lua: -------------------------------------------------------------------------------- 1 | --- Eva status module. 2 | -- Have logic to proceed status effects, like passive or 3 | -- temporary bonuses. Carry on time logic, throw logic events 4 | -- @submodule eva 5 | 6 | 7 | local M = {} 8 | 9 | 10 | function M.add_status() 11 | 12 | end 13 | 14 | 15 | function M.remove_status() 16 | 17 | end 18 | 19 | 20 | function M.is_status() 21 | 22 | end 23 | 24 | 25 | function M.get_stacks() 26 | 27 | end 28 | 29 | 30 | function M.get_max_stacks() 31 | 32 | end 33 | 34 | 35 | function M.skip_duration_time() 36 | 37 | end 38 | 39 | 40 | return M 41 | -------------------------------------------------------------------------------- /eva/scripts/hexgrid_handler.lua: -------------------------------------------------------------------------------- 1 | -- Handler for hexagonal grid astar 2 | 3 | local const = require("eva.const") 4 | local basic_handler = require("eva.libs.astar.basic_handler") 5 | 6 | local M = {} 7 | 8 | 9 | function M.new_handler(get_node_fn, options) 10 | local data = basic_handler.get_handler(get_node_fn) 11 | 12 | data.neighbors = const.ASTAR.HEX.NEIGHBORS 13 | 14 | data.get_neighbors = function(map_handler, x, y) 15 | return map_handler.neighbors[(bit.band(y, 1)) + 1] 16 | end 17 | 18 | return setmetatable(data, { __index = basic_handler }) 19 | end 20 | 21 | 22 | return M 23 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/insality_games.go: -------------------------------------------------------------------------------- 1 | components { 2 | id: "insality_games" 3 | component: "/gui/ui_logo_insality_games/insality_games.gui" 4 | position { 5 | x: 0.0 6 | y: 0.0 7 | z: 0.0 8 | } 9 | rotation { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 1.0 14 | } 15 | } 16 | components { 17 | id: "insality_games_script" 18 | component: "/gui/ui_logo_insality_games/insality_games.script" 19 | position { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | } 24 | rotation { 25 | x: 0.0 26 | y: 0.0 27 | z: 0.0 28 | w: 1.0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /eva/materials/greyscale_gui/greyscale_gui.material: -------------------------------------------------------------------------------- 1 | name: "gui" 2 | tags: "gui" 3 | vertex_program: "/eva/materials/greyscale_gui/greyscale_gui.vp" 4 | fragment_program: "/eva/materials/greyscale_gui/greyscale_gui.fp" 5 | vertex_space: VERTEX_SPACE_WORLD 6 | vertex_constants { 7 | name: "view_proj" 8 | type: CONSTANT_TYPE_VIEWPROJ 9 | value { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 0.0 14 | } 15 | } 16 | samplers { 17 | name: "texture_sampler" 18 | wrap_u: WRAP_MODE_CLAMP_TO_EDGE 19 | wrap_v: WRAP_MODE_CLAMP_TO_EDGE 20 | filter_min: FILTER_MODE_MIN_LINEAR 21 | filter_mag: FILTER_MODE_MAG_LINEAR 22 | } 23 | -------------------------------------------------------------------------------- /eva/modules/render.lua: -------------------------------------------------------------------------------- 1 | --- Eva render module 2 | -- Can adjust different game render settings 3 | -- and effects 4 | -- @submodule eva 5 | 6 | 7 | local M = {} 8 | 9 | 10 | --- Change render 11 | -- @function eva.render.set_blur 12 | function M.set_blur(power) 13 | 14 | end 15 | 16 | 17 | --- Change render 18 | -- @function eva.render.set_light 19 | function M.set_light(power) 20 | 21 | end 22 | 23 | 24 | --- Change render 25 | -- @function eva.render.set_vignette 26 | function M.set_vignette(power) 27 | 28 | end 29 | 30 | 31 | function M.set_clear_color(color) 32 | msg.post("@render:", "clear_color", { color = color }) 33 | end 34 | 35 | 36 | return M 37 | -------------------------------------------------------------------------------- /eva/resources/quests_settings_example.lua: -------------------------------------------------------------------------------- 1 | -- Quest settings example 2 | local M = {} 3 | 4 | 5 | function M.is_can_start(quest_id, quest) 6 | return true 7 | end 8 | 9 | 10 | function M.is_can_complete(quest_id, quest) 11 | return true 12 | end 13 | 14 | 15 | function M.is_can_event(quest_id, quest) 16 | return true 17 | end 18 | 19 | 20 | function M.on_quest_start(quest_id, quest) 21 | 22 | end 23 | 24 | 25 | function M.on_quest_progress(quest_id, quest) 26 | 27 | end 28 | 29 | 30 | function M.on_quest_task_completed(quest_id, quest) 31 | 32 | end 33 | 34 | 35 | function M.on_quest_completed(quest_id, quest) 36 | 37 | end 38 | 39 | 40 | return M 41 | -------------------------------------------------------------------------------- /eva/scripts/isogrid_handler.lua: -------------------------------------------------------------------------------- 1 | -- Handler for isometric grid astar 2 | 3 | local const = require("eva.const") 4 | local basic_handler = require("eva.libs.astar.basic_handler") 5 | 6 | local M = {} 7 | 8 | 9 | function M.new_handler(get_node_fn, options) 10 | local data = basic_handler.get_handler(get_node_fn) 11 | 12 | data.neighbors = const.ASTAR.ISO.NEIGHBORS 13 | if options and options.diagonal then 14 | data.neighbors = const.ASTAR.ISO.NEIGHBORS_DIAGONAL 15 | end 16 | 17 | data.get_neighbors = function(map_handler, x, y) 18 | return map_handler.neighbors[(bit.band(y, 1)) + 1] 19 | end 20 | 21 | return setmetatable(data, { __index = basic_handler }) 22 | end 23 | 24 | 25 | return M 26 | -------------------------------------------------------------------------------- /eva/modules/social/stub.lua: -------------------------------------------------------------------------------- 1 | --- Stub platform module adapter 2 | -- @module platform.stub 3 | -- @local 4 | 5 | local class = require("eva.libs.middleclass") 6 | 7 | local M = class("eva.social.stub") 8 | 9 | 10 | function M:login() 11 | error("abstact method") 12 | end 13 | 14 | 15 | function M:logout() 16 | error("abstact method") 17 | end 18 | 19 | 20 | function M:is_logged() 21 | error("abstact method") 22 | end 23 | 24 | 25 | function M:get_id() 26 | error("abstact method") 27 | end 28 | 29 | 30 | function M:get_username() 31 | error("abstact method") 32 | end 33 | 34 | 35 | function M:after_eva_init() 36 | error("abstact method") 37 | end 38 | 39 | 40 | return M 41 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/insality_games.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/gui/ui_logo_insality_games/images/Games.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/gui/ui_logo_insality_games/images/Heart.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/gui/ui_logo_insality_games/images/Insality.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/images/empty.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | images { 18 | image: "/assets/images/pixel.png" 19 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 20 | } 21 | margin: 0 22 | extrude_borders: 2 23 | inner_padding: 0 24 | max_page_width: 0 25 | max_page_height: 0 26 | -------------------------------------------------------------------------------- /eva/materials/sprite.material: -------------------------------------------------------------------------------- 1 | name: "sprite" 2 | tags: "tile" 3 | vertex_program: "/builtins/materials/sprite.vp" 4 | fragment_program: "/builtins/materials/sprite.fp" 5 | vertex_space: VERTEX_SPACE_WORLD 6 | vertex_constants { 7 | name: "view_proj" 8 | type: CONSTANT_TYPE_VIEWPROJ 9 | value { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 0.0 14 | } 15 | } 16 | fragment_constants { 17 | name: "tint" 18 | type: CONSTANT_TYPE_USER 19 | value { 20 | x: 1.0 21 | y: 1.0 22 | z: 1.0 23 | w: 1.0 24 | } 25 | } 26 | samplers { 27 | name: "texture_sampler" 28 | wrap_u: WRAP_MODE_REPEAT 29 | wrap_v: WRAP_MODE_REPEAT 30 | filter_min: FILTER_MODE_MIN_LINEAR 31 | filter_mag: FILTER_MODE_MAG_LINEAR 32 | } 33 | -------------------------------------------------------------------------------- /gui/window_info/window_info.collection: -------------------------------------------------------------------------------- 1 | name: "window_info" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"window_info\"\n" 7 | " component: \"/gui/window_info/window_info.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "" 23 | position { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | } 28 | rotation { 29 | x: 0.0 30 | y: 0.0 31 | z: 0.0 32 | w: 1.0 33 | } 34 | scale3 { 35 | x: 1.0 36 | y: 1.0 37 | z: 1.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gui/window_admin/window_admin.collection: -------------------------------------------------------------------------------- 1 | name: "window_admin" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"window_admin\"\n" 7 | " component: \"/gui/window_admin/window_admin.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "" 23 | position { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | } 28 | rotation { 29 | x: 0.0 30 | y: 0.0 31 | z: 0.0 32 | w: 1.0 33 | } 34 | scale3 { 35 | x: 1.0 36 | y: 1.0 37 | z: 1.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /export_config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "sheets":[{ 3 | "name": "data", 4 | "type": "csv_web", 5 | "id": "19MitV3fl_bIrnVgeo2GzZRIhkKNotNXQ1QRzVO4Hzgk", 6 | "rule": "./rules_data.json", 7 | "handlers": ["./custom_handlers.js"], 8 | "all_handlers": [ 9 | { 10 | "type": "convert_array" 11 | }, 12 | { 13 | "type": "convert_boolean" 14 | } 15 | ], 16 | "save": [{ 17 | "dist": "../resources/json/", 18 | "format": "json" 19 | }], 20 | "wrap_with_name": true 21 | }, 22 | { 23 | "name": "data", 24 | "type": "csv_web", 25 | "id": "19MitV3fl_bIrnVgeo2GzZRIhkKNotNXQ1QRzVO4Hzgk", 26 | "rule": "./rules_locale.json", 27 | "save": [{ 28 | "dist": "../resources/locale/", 29 | "format": "json" 30 | }], 31 | "wrap_with_name": true 32 | }] 33 | } 34 | -------------------------------------------------------------------------------- /gui/window_confirm/window_confirm.collection: -------------------------------------------------------------------------------- 1 | name: "window_confirm" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"window_confirm\"\n" 7 | " component: \"/gui/window_confirm/window_confirm.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "" 23 | position { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | } 28 | rotation { 29 | x: 0.0 30 | y: 0.0 31 | z: 0.0 32 | w: 1.0 33 | } 34 | scale3 { 35 | x: 1.0 36 | y: 1.0 37 | z: 1.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gui/window_settings/window_settings.collection: -------------------------------------------------------------------------------- 1 | name: "window_settings" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"window_settings\"\n" 7 | " component: \"/gui/window_settings/window_settings.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "" 23 | position { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | } 28 | rotation { 29 | x: 0.0 30 | y: 0.0 31 | z: 0.0 32 | w: 1.0 33 | } 34 | scale3 { 35 | x: 1.0 36 | y: 1.0 37 | z: 1.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gui/window_template/window_template.collection: -------------------------------------------------------------------------------- 1 | name: "window_template" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"window_template\"\n" 7 | " component: \"/gui/window_template/window_template.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | " property_decls {\n" 20 | " }\n" 21 | "}\n" 22 | "" 23 | position { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | } 28 | rotation { 29 | x: 0.0 30 | y: 0.0 31 | z: 0.0 32 | w: 1.0 33 | } 34 | scale3 { 35 | x: 1.0 36 | y: 1.0 37 | z: 1.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /eva/materials/greyscale_gui/greyscale_gui.fp: -------------------------------------------------------------------------------- 1 | varying mediump vec2 var_texcoord0; 2 | varying lowp vec4 var_color; 3 | varying lowp float var_saturate_adjust; 4 | 5 | uniform lowp sampler2D texture_sampler; 6 | 7 | // FROM: https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Builtin/Functions/saturation.glsl 8 | vec3 czm_saturation(vec3 rgb, float adjustment) 9 | { 10 | // Algorithm from Chapter 16 of OpenGL Shading Language 11 | const vec3 W = vec3(0.2125, 0.7154, 0.0721); 12 | vec3 intensity = vec3(dot(rgb, W)); 13 | return mix(intensity, rgb, adjustment); 14 | } 15 | 16 | void main() 17 | { 18 | lowp vec4 tex = texture2D(texture_sampler, var_texcoord0.xy); 19 | vec4 c = tex * var_color; 20 | c.rgb = czm_saturation(c.rgb, var_saturate_adjust); 21 | gl_FragColor = c; 22 | } 23 | -------------------------------------------------------------------------------- /eva/modules/share.lua: -------------------------------------------------------------------------------- 1 | --- Eva share module. This module require next dependencies (see dependencies.txt): 2 | -- defold-sharing 3 | -- defold-screenshot 4 | -- @submodule eva 5 | 6 | local log = require("eva.log") 7 | 8 | local logger = log.get_logger("eva.share") 9 | 10 | local M = {} 11 | 12 | 13 | --- Share screenshot of the game 14 | -- @function eva.share.screen 15 | -- @tparam string text The optional text to share with screenshot 16 | function M.screen(text) 17 | if not M.is_supported() then 18 | logger:info("The eva.share module is not available on this platform") 19 | end 20 | 21 | local buffer = screenshot.png() 22 | share.image(buffer, text) 23 | end 24 | 25 | 26 | function M.is_supported() 27 | if share and screenshot then 28 | return true 29 | end 30 | return false 31 | end 32 | 33 | 34 | return M 35 | -------------------------------------------------------------------------------- /eva/modules/iaps/iaps_mobile.lua: -------------------------------------------------------------------------------- 1 | local app = require("eva.app") 2 | local log = require("eva.log") 3 | local luax = require("eva.luax") 4 | local const = require("eva.const") 5 | 6 | local logger = log.get_logger("iap_mobile") 7 | 8 | 9 | local Iaps = {} 10 | 11 | 12 | local function iap_listener(self, transaction, error) 13 | if error then 14 | logger:error("Iap listener error", { error = error }) 15 | end 16 | end 17 | 18 | 19 | function Iaps.init() 20 | iap.set_listener(iap_listener) 21 | end 22 | 23 | 24 | function Iaps.buy(iap_ident) 25 | iap.buy(iap_ident) 26 | end 27 | 28 | 29 | function Iaps.consume(transaction) 30 | 31 | end 32 | 33 | 34 | function Iaps.fetch_products() 35 | iap.list(luax.table.list(app.iap_products, "ident"), list_callback) 36 | end 37 | 38 | 39 | function Iaps.restore() 40 | iap.restore() 41 | end 42 | 43 | 44 | return Iaps 45 | -------------------------------------------------------------------------------- /eva/app.lua: -------------------------------------------------------------------------------- 1 | --- Eva app instance module 2 | -- contains all runtime data of every module 3 | -- @local 4 | 5 | local luax = require("eva.luax") 6 | local const = require("eva.const") 7 | 8 | local app = {} 9 | local M = {} 10 | 11 | 12 | --- Clear the app state or the app value 13 | function M.clear(value) 14 | if not value then 15 | app = {} 16 | else 17 | app[value] = nil 18 | end 19 | end 20 | 21 | 22 | M = setmetatable(M, { 23 | __newindex = function(self, key, value) 24 | if type(value) ~= const.TYPE.TABLE then 25 | error("You can add to eva.app only tables value. Key: " .. key) 26 | end 27 | 28 | if app[key] and luax.table.length(app[key]) > 0 then 29 | error("Trying to override not empty eva.app data. Key: " .. key) 30 | end 31 | 32 | app[key] = value 33 | end, 34 | 35 | __index = function(self, key) 36 | return app[key] 37 | end 38 | }) 39 | 40 | 41 | return M 42 | -------------------------------------------------------------------------------- /game/system/camera.go: -------------------------------------------------------------------------------- 1 | components { 2 | id: "script" 3 | component: "/rendercam/camera.script" 4 | position { 5 | x: 0.0 6 | y: 0.0 7 | z: 0.0 8 | } 9 | rotation { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 1.0 14 | } 15 | properties { 16 | id: "nearZ" 17 | value: "-100.0" 18 | type: PROPERTY_TYPE_NUMBER 19 | } 20 | properties { 21 | id: "farZ" 22 | value: "100.0" 23 | type: PROPERTY_TYPE_NUMBER 24 | } 25 | properties { 26 | id: "useViewArea" 27 | value: "true" 28 | type: PROPERTY_TYPE_BOOLEAN 29 | } 30 | properties { 31 | id: "viewArea" 32 | value: "900.0, 900.0, 0.0" 33 | type: PROPERTY_TYPE_VECTOR3 34 | } 35 | properties { 36 | id: "fixedArea" 37 | value: "false" 38 | type: PROPERTY_TYPE_BOOLEAN 39 | } 40 | properties { 41 | id: "fixedHeight" 42 | value: "true" 43 | type: PROPERTY_TYPE_BOOLEAN 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /resources/game.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "gamedata.proto"; 3 | 4 | package game; 5 | 6 | 7 | message ProfileData { 8 | string user_id = 1; 9 | string user_code = 2; 10 | TutorialData tutorial_data = 3; 11 | } 12 | 13 | 14 | message TutorialData { 15 | bool is_enabled = 1; 16 | } 17 | 18 | 19 | message PlatformData { 20 | message YandexData { 21 | string user_id = 1; 22 | bool is_connected = 2; 23 | string name = 3; 24 | } 25 | 26 | message AppleData { 27 | string user_id = 1; 28 | bool is_connected = 2; 29 | string name = 3; 30 | } 31 | 32 | message GoogleData { 33 | string user_id = 1; 34 | bool is_connected = 2; 35 | string name = 3; 36 | } 37 | 38 | message FacebookData { 39 | string user_id = 1; 40 | bool is_connected = 2; 41 | string name = 3; 42 | } 43 | 44 | YandexData yandex_data = 1; 45 | AppleData apple_data = 2; 46 | GoogleData google_data = 3; 47 | FacebookData facebook_data = 4; 48 | } 49 | -------------------------------------------------------------------------------- /assets/fonts/game.font: -------------------------------------------------------------------------------- 1 | font: "/assets/fonts/troika.otf" 2 | material: "/builtins/fonts/font-df.material" 3 | size: 65 4 | antialias: 1 5 | alpha: 1.0 6 | outline_alpha: 1.0 7 | outline_width: 6.0 8 | shadow_alpha: 0.0 9 | shadow_blur: 0 10 | shadow_x: 0.0 11 | shadow_y: 0.0 12 | extra_characters: "\320\260\320\261\320\262\320\263\320\264\320\265\321\221\320\266\320\267\320\270\320\271\320\272\320\273\320\274\320\275\320\276\320\277\321\200\321\201\321\202\321\203\321\204\321\205\321\206\321\207\321\210\321\211\321\212\321\213\321\214\321\215\321\216\321\217\320\220\320\221\320\222\320\223\320\224\320\225\320\201\320\226\320\227\320\230\320\231\320\232\320\233\320\234\320\235\320\236\320\237\320\240\320\241\320\242\320\243\320\244\320\245\320\246\320\247\320\250\320\251\320\252\320\253\320\254\320\255\320\256\320\257.z\302\247,.``\'\303\227`\'" 13 | output_format: TYPE_DISTANCE_FIELD 14 | all_chars: false 15 | cache_width: 0 16 | cache_height: 0 17 | render_mode: MODE_MULTI_LAYER 18 | -------------------------------------------------------------------------------- /eva/modules/ads/ads_adapter_interface.lua: -------------------------------------------------------------------------------- 1 | --- The ads adapter stub 2 | -- @module adapter.stub 3 | -- @local 4 | 5 | local Ads = {} 6 | 7 | --- Init the ads adapter 8 | -- @function adapter.initialize 9 | -- @tparam string ads_id 10 | -- @tparam function on_ready_callback 11 | -- @local 12 | function Ads.initialize(ads_id, on_ready_callback) end 13 | 14 | 15 | --- Check if ads on adapter is ready 16 | -- @function adapter.is_ready 17 | -- @tparam string ads_id 18 | -- @tparam table ad_config 19 | -- @treturn boolean 20 | -- @local 21 | function Ads.is_ready(ad_id, ad_config) end 22 | 23 | 24 | --- Show ads 25 | -- @function adapter.show 26 | -- @tparam string ad_id 27 | -- @tparam table ad_config 28 | -- @tparam function success_callback The callback on ads success show 29 | -- @tparam function error_callback The callback on ads failure show 30 | -- @local 31 | function Ads.show(ad_id, ad_config, success_callback, finish_callback, error_callback) end 32 | 33 | 34 | return Ads 35 | -------------------------------------------------------------------------------- /eva/modules/rate/rate_yandex.lua: -------------------------------------------------------------------------------- 1 | local log = require("eva.log") 2 | local const = require("eva.const") 3 | 4 | local yagames = const.require("yagames.yagames") 5 | 6 | local logger = log.get_logger("rate_yd") 7 | 8 | local Rate = {} 9 | 10 | 11 | function Rate.is_supported() 12 | return true 13 | end 14 | 15 | 16 | function Rate.is_can_show(callback) 17 | assert(callback) 18 | 19 | if not yagames.ysdk_ready then 20 | logger:info("The ysdk is not ready to promt rate") 21 | return callback(false) 22 | end 23 | 24 | yagames.feedback_can_review(function(self, err, result) 25 | logger:info("The result of yandex can review", { err = err, result = result }) 26 | return callback(result.value) 27 | end) 28 | end 29 | 30 | 31 | function Rate.request_review() 32 | logger:info("Request yandex review") 33 | yagames.feedback_request_review(function(self, err, result) 34 | logger:info("Feedback sent", { err = err, is_send = result.feedbackSent }) 35 | end) 36 | end 37 | 38 | 39 | 40 | return Rate 41 | -------------------------------------------------------------------------------- /assets/scenes/scene_menu.collection: -------------------------------------------------------------------------------- 1 | name: "scene_menu" 2 | instances { 3 | id: "camera_menu" 4 | prototype: "/game/system/camera.go" 5 | position { 6 | x: 694.0 7 | y: 647.0 8 | z: 0.0 9 | } 10 | rotation { 11 | x: 0.0 12 | y: 0.0 13 | z: 0.0 14 | w: 1.0 15 | } 16 | scale3 { 17 | x: 1.0 18 | y: 1.0 19 | z: 1.0 20 | } 21 | } 22 | scale_along_z: 0 23 | embedded_instances { 24 | id: "gui" 25 | data: "components {\n" 26 | " id: \"scene_game\"\n" 27 | " component: \"/gui/scene_menu/scene_menu.gui\"\n" 28 | " position {\n" 29 | " x: 0.0\n" 30 | " y: 0.0\n" 31 | " z: 0.0\n" 32 | " }\n" 33 | " rotation {\n" 34 | " x: 0.0\n" 35 | " y: 0.0\n" 36 | " z: 0.0\n" 37 | " w: 1.0\n" 38 | " }\n" 39 | " property_decls {\n" 40 | " }\n" 41 | "}\n" 42 | "" 43 | position { 44 | x: 0.0 45 | y: 0.0 46 | z: 0.0 47 | } 48 | rotation { 49 | x: 0.0 50 | y: 0.0 51 | z: 0.0 52 | w: 1.0 53 | } 54 | scale3 { 55 | x: 1.0 56 | y: 1.0 57 | z: 1.0 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /eva/modules/social.lua: -------------------------------------------------------------------------------- 1 | --- Social Eva module 2 | -- Handle any social connection 3 | -- For example: Facebook, Google Play Game Services and Game Center 4 | -- @submodule eva 5 | 6 | local app = require("eva.app") 7 | local SocialStub = require("eva.modules.social.stub") 8 | local SocialGPGS = require("eva.modules.social.gpgs") 9 | 10 | local device = require("eva.modules.device") 11 | 12 | local M = {} 13 | 14 | 15 | function M.login_platform() 16 | app._social_data.platform:login() 17 | end 18 | 19 | 20 | function M.logout_platform() 21 | app._social_data.platform:logout() 22 | end 23 | 24 | 25 | function M.is_logged() 26 | return app._social_data.platform:is_logged() 27 | end 28 | 29 | 30 | function M.login_toggle_platform() 31 | if M.is_logged() then 32 | M.logout_platform() 33 | else 34 | M.login_platform() 35 | end 36 | end 37 | 38 | 39 | function M.after_eva_init() 40 | app._social_data = { 41 | platform = SocialStub() 42 | } 43 | 44 | if device.is_android() and gpgs then 45 | app._social_data.platform = SocialGPGS() 46 | end 47 | end 48 | 49 | 50 | return M 51 | -------------------------------------------------------------------------------- /eva/libs/b64.lua: -------------------------------------------------------------------------------- 1 | -- base64 encode/decode (http://lua-users.org/wiki/BaseSixtyFour) 2 | 3 | local M = {} 4 | 5 | local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 6 | 7 | function M.encode(data) 8 | return ((data:gsub('.', function(x) 9 | local r,b='',x:byte() 10 | for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end 11 | return r; 12 | end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) 13 | if (#x < 6) then return '' end 14 | local c=0 15 | for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end 16 | return b:sub(c+1,c+1) 17 | end)..({ '', '==', '=' })[#data%3+1]) 18 | end 19 | 20 | function M.decode(data) 21 | data = string.gsub(data, '[^'..b..'=]', '') 22 | return (data:gsub('.', function(x) 23 | if (x == '=') then return '' end 24 | local r,f='',(b:find(x)-1) 25 | for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end 26 | return r; 27 | end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) 28 | if (#x ~= 8) then return '' end 29 | local c=0 30 | for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end 31 | return string.char(c) 32 | end)) 33 | end 34 | 35 | return M 36 | -------------------------------------------------------------------------------- /assets/atlases/window_settings.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/images/window_settings/i_music_off.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/images/window_settings/i_music_on.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/images/window_settings/i_notif_off.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/images/window_settings/i_notif_on.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | images { 18 | image: "/assets/images/window_settings/i_sound_off.png" 19 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 20 | } 21 | images { 22 | image: "/assets/images/window_settings/i_sound_on.png" 23 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 24 | } 25 | images { 26 | image: "/assets/images/window_settings/i_vibro_off.png" 27 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 28 | } 29 | images { 30 | image: "/assets/images/window_settings/i_vibro_on.png" 31 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 32 | } 33 | margin: 0 34 | extrude_borders: 2 35 | inner_padding: 0 36 | max_page_width: 0 37 | max_page_height: 0 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Maxim Tuprikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gui/ui_logo_insality_games/insality_games.gui_script: -------------------------------------------------------------------------------- 1 | local animation = require("gui.ui_logo_insality_games.animation_insality_games") 2 | 3 | local function hide_scene(self) 4 | if self.is_hiding then 5 | return 6 | end 7 | 8 | self.is_hiding = true 9 | local root = gui.get_node("root") 10 | gui.animate(root, "color.w", 0, gui.EASING_OUTSINE, 0.25, 0, function() 11 | msg.post(".", "destroy_go") 12 | end) 13 | end 14 | 15 | 16 | function init(self) 17 | gui.set_render_order(15) 18 | 19 | self.animation = animation.create(nil, { 20 | animation_id = "appear", 21 | }, true) 22 | self._timer_id = timer.delay(2.75, false, hide_scene) 23 | 24 | msg.post(".", "late_acquire_focus") 25 | end 26 | 27 | 28 | function final(self) 29 | msg.post(".", "release_input_focus") 30 | end 31 | 32 | 33 | function on_message(self, message_id, message, sender) 34 | if message_id == hash("late_acquire_focus") then 35 | animation.play(self.animation) 36 | msg.post(".", "acquire_input_focus") 37 | end 38 | end 39 | 40 | 41 | function on_input(self, action_id, action) 42 | if not self.is_hiding and action and action.released then 43 | hide_scene(self) 44 | end 45 | 46 | return true 47 | end 48 | -------------------------------------------------------------------------------- /eva/libs/sitelock.lua: -------------------------------------------------------------------------------- 1 | -- Source: https://github.com/indiesoftby/defold-yagames/blob/master/yagames/sitelock.lua 2 | -- @module sitelock 3 | 4 | local M = {} 5 | 6 | M.domains = { "localhost" } 7 | 8 | local function ends_with(s, substr) 9 | return s:sub(-#substr) == substr 10 | end 11 | 12 | --- Adds a domain to the list 13 | -- @tparam string domain 14 | function M.add_domain(domain) 15 | table.insert(M.domains, domain) 16 | end 17 | 18 | --- Compares the current hostname to the domains from the list. 19 | -- @treturn boolean 20 | function M.verify_domain() 21 | if not html5 then 22 | return true 23 | end 24 | local current_domain = html5.run("window.location.hostname") 25 | for key, value in ipairs(M.domains) do 26 | if value == current_domain or ends_with(current_domain, "." .. value) then 27 | return true 28 | end 29 | end 30 | return false 31 | end 32 | 33 | --- Returns the current domain name (hostname). 34 | -- @treturn string 35 | function M.get_current_domain() 36 | if html5 then 37 | return html5.run("window.location.hostname") 38 | else 39 | return "" 40 | end 41 | end 42 | 43 | --- Checks the build type 44 | -- @treturn boolean 45 | function M.is_release_build() 46 | return not sys.get_engine_info().is_debug 47 | end 48 | 49 | return M 50 | -------------------------------------------------------------------------------- /eva/modules/social/gpgs.lua: -------------------------------------------------------------------------------- 1 | --- Gpgs platform module adapter 2 | -- @module platform.gpgs 3 | -- @local 4 | local class = require("eva.libs.middleclass") 5 | local SocialStub = require("eva.modules.social.stub") 6 | 7 | local M = class("eva.social.gpgs", SocialStub) 8 | 9 | 10 | function M:initialize() 11 | gpgs.set_callback(self._gpgs_callback) 12 | gpgs.silent_login() 13 | end 14 | 15 | 16 | function M:login() 17 | gpgs.login() 18 | end 19 | 20 | 21 | function M:logout() 22 | gpgs.logout() 23 | end 24 | 25 | 26 | function M:is_logged() 27 | return gpgs.is_logged_in() 28 | end 29 | 30 | 31 | function M:get_id() 32 | return gpgs.get_id() 33 | end 34 | 35 | 36 | function M:get_username() 37 | return gpgs.get_display_name() 38 | end 39 | 40 | 41 | function M:_gpgs_callback(_, message_id, message) 42 | if message_id == gpgs.MSG_SIGN_IN or message_id == gpgs.MSG_SILENT_SIGN_IN then 43 | if message.status == gpgs.STATUS_SUCCESS then 44 | print("Signed in") 45 | print(gpgs.get_id()) 46 | print(gpgs.get_display_name()) 47 | else 48 | print("Sign in error!") 49 | pprint(message) 50 | end 51 | elseif message_id == gpgs.MSG_SIGN_OUT then 52 | print("Signed out") 53 | end 54 | pprint("Message:", message_id, message) 55 | end 56 | 57 | 58 | return M 59 | -------------------------------------------------------------------------------- /gui/ui_scene_transition/ui_scene_transition.gui_script: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") 2 | local const = require("game.const") 3 | local druid = require("druid.druid") 4 | 5 | local UiSceneTransition = require("gui.ui_scene_transition.ui_scene_transition") 6 | 7 | 8 | function init(self) 9 | self.druid = druid.new(self) 10 | self.ui_scene_transition = self.druid:new(UiSceneTransition) ---@type ui_scene_transition 11 | self._is_enabled = false 12 | 13 | gui.set_render_order(const.RENDER_ORDER.TRANSITION) 14 | end 15 | 16 | function final(self) 17 | self.druid:final() 18 | end 19 | 20 | 21 | function on_input(self) 22 | return true 23 | end 24 | 25 | 26 | function on_message(self, message_id, message, sender) 27 | self.druid:on_message(message_id, message, sender) 28 | if message_id == const.MSG.TRANSITION then 29 | self._is_enabled = message.is_enable 30 | 31 | if self._is_enabled then 32 | eva.events.event(const.EVENT.MAP_TRANSITION, { is_start = true }) 33 | msg.post(".", "acquire_input_focus") 34 | end 35 | 36 | self.ui_scene_transition:set_loading(self._is_enabled, function() 37 | if not self._is_enabled then 38 | eva.events.event(const.EVENT.MAP_TRANSITION, { is_finish = true }) 39 | msg.post(".", "release_input_focus") 40 | end 41 | end) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /eva/modules/gdpr.lua: -------------------------------------------------------------------------------- 1 | --- Eva GDPR module. 2 | -- Can process stuff with GDPR 3 | -- @submodule eva 4 | 5 | 6 | local log = require("eva.log") 7 | local app = require("eva.app") 8 | local const = require("eva.const") 9 | 10 | local game = require("eva.modules.game") 11 | local proto = require("eva.modules.proto") 12 | local saver = require("eva.modules.saver") 13 | 14 | local logger = log.get_logger("eva.gdpr") 15 | 16 | local M = {} 17 | 18 | 19 | --- Apply the GDPR to the game profile 20 | -- @function eva.gdpr.apply_gdpr 21 | function M.apply_gdpr() 22 | app[const.EVA.GDPR].is_accepted = true 23 | app[const.EVA.GDPR].accept_date = game.get_current_time_string() 24 | 25 | logger:info("Apply GDPR", { date = app[const.EVA.GDPR].accept_date }) 26 | end 27 | 28 | 29 | --- Return if GDPR is accepted 30 | -- @function eva.gdpr.is_accepted 31 | function M.is_accepted() 32 | return app[const.EVA.GDPR].is_accepted 33 | end 34 | 35 | 36 | --- Open the policy URL 37 | -- @function eva.gdpr.open_policy_url 38 | function M.open_policy_url() 39 | local settings = app.settings.gdpr 40 | sys.open_url(settings.policy_url) 41 | end 42 | 43 | 44 | function M.on_eva_init() 45 | app[const.EVA.GDPR] = proto.get(const.EVA.GDPR) 46 | saver.add_save_part(const.EVA.GDPR, app[const.EVA.GDPR]) 47 | end 48 | 49 | 50 | return M 51 | -------------------------------------------------------------------------------- /eva/modules/feature.lua: -------------------------------------------------------------------------------- 1 | --- Eva feature can control which feature is enabled now and can 2 | -- be changed in runtime due different conditions. 3 | -- @submodule eva 4 | 5 | 6 | local app = require("eva.app") 7 | local log = require("eva.log") 8 | local const = require("eva.const") 9 | 10 | local proto = require("eva.modules.proto") 11 | local saver = require("eva.modules.saver") 12 | local utils = require("eva.modules.utils") 13 | 14 | local logger = log.get_logger("eva.feature") 15 | 16 | local M = {} 17 | 18 | 19 | --- Toggle manually feature 20 | -- @function eva.feature.toggle 21 | -- @tparam string feature_name Feature name 22 | -- @tparam bool state Feature state 23 | function M.set_enabled(feature_name, state) 24 | 25 | end 26 | 27 | 28 | --- Start eva feature system. 29 | -- Need to call, when game is prepared for features 30 | -- @function eva.feature.start 31 | function M.start() 32 | app.feature_status.is_started = true 33 | end 34 | 35 | 36 | function M.before_eva_init() 37 | 38 | end 39 | 40 | function M.on_eva_init() 41 | app[const.EVA.FEATURE] = proto.get(const.EVA.FEATURE) 42 | saver.add_save_part(const.EVA.FEATURE, app[const.EVA.FEATURE]) 43 | end 44 | 45 | 46 | function M.after_eva_init() 47 | app.feature_status = { 48 | is_started = false, 49 | } 50 | end 51 | 52 | 53 | 54 | 55 | return M 56 | -------------------------------------------------------------------------------- /eva/modules/rating.lua: -------------------------------------------------------------------------------- 1 | --- Eva rating module 2 | -- Can carry for calculate ratings for different 3 | -- game systems. Elo for example 4 | -- @submodule eva 5 | 6 | 7 | local app = require("eva.app") 8 | local luax = require("eva.luax") 9 | 10 | local M = {} 11 | 12 | 13 | local function diff(rating_a, rating_b) 14 | local value = (rating_b - rating_a) / app.settings.rating.diff_constant 15 | return 1 / (1 + math.pow(10, value)) 16 | end 17 | 18 | 19 | --- Call elo rating 20 | -- @function eva.rating.elo 21 | -- @tparam number rating_a Player rating 22 | -- @tparam number rating_b Opponent rating 23 | -- @tparam number game_koef Result of game. 1 is win, 0 on loose, 0.5 is draw 24 | function M.elo(rating_a, rating_b, game_koef, K) 25 | assert(game_koef, "You should provide game_koef status") 26 | local settings = app.settings.rating 27 | 28 | K = K or settings.rating_diff 29 | 30 | local rating_diff = K * (game_koef - diff(rating_a, rating_b)) 31 | 32 | if (rating_diff < 0 and rating_a < settings.easy_max) then 33 | -- On loose and low rating, decrease elo in FREE_REDUCE_KOEF times less 34 | rating_diff = rating_diff / settings.easy_reduce_koef 35 | end 36 | 37 | local new_rating = luax.math.round(rating_a + rating_diff) 38 | return luax.math.clamp(new_rating, settings.min, settings.max) 39 | end 40 | 41 | 42 | return M 43 | -------------------------------------------------------------------------------- /resources/locale/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "game_title": "Template", 3 | "lang_name": "English", 4 | "lang_name_de": "Deutsch", 5 | "lang_name_en": "English", 6 | "lang_name_es": "Español", 7 | "lang_name_fr": "Français", 8 | "lang_name_it": "Italiano", 9 | "lang_name_ru": "Русский", 10 | "time_format_d": "%id %02ih", 11 | "time_format_h": "%ih %02im", 12 | "time_format_m": "%i:%02i", 13 | "ui_ads_desc": "Running ads...", 14 | "ui_agree": "Agree", 15 | "ui_cancel": "Cancel", 16 | "ui_completed": "Completed", 17 | "ui_exit": "Exit", 18 | "ui_header_confirm": "Confirmation", 19 | "ui_header_info": "Information", 20 | "ui_header_settings": "Settings", 21 | "ui_label_off": "Off", 22 | "ui_label_on": "On", 23 | "ui_language": "Language", 24 | "ui_login": "Login", 25 | "ui_menu": "Menu", 26 | "ui_music": "Music", 27 | "ui_no_ads": "NO ADS", 28 | "ui_notifications": "Notifications", 29 | "ui_open": "Open", 30 | "ui_play": "Play", 31 | "ui_rate_us": "Rate", 32 | "ui_restore": "Restore", 33 | "ui_select": "Select", 34 | "ui_share": "Share", 35 | "ui_social": "Social", 36 | "ui_sound": "Sound", 37 | "ui_support_thanks": "Thanks for support!", 38 | "ui_time_days": "day", 39 | "ui_time_hours": "hour", 40 | "ui_time_minutes": "min", 41 | "ui_time_seconds": "sec", 42 | "ui_vibro": "Vibration" 43 | } -------------------------------------------------------------------------------- /assets/atlases/window_general.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/images/window_general/background_window.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/images/window_general/button_blue.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/images/window_general/button_close.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/images/window_general/button_green.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | images { 18 | image: "/assets/images/window_general/button_grey.png" 19 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 20 | } 21 | images { 22 | image: "/assets/images/window_general/button_purple.png" 23 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 24 | } 25 | images { 26 | image: "/assets/images/window_general/button_red.png" 27 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 28 | } 29 | images { 30 | image: "/assets/images/window_general/button_yellow.png" 31 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 32 | } 33 | images { 34 | image: "/assets/images/empty.png" 35 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 36 | } 37 | images { 38 | image: "/assets/images/pixel.png" 39 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 40 | } 41 | margin: 0 42 | extrude_borders: 2 43 | inner_padding: 0 44 | max_page_width: 0 45 | max_page_height: 0 46 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | std = "max" 2 | files['.luacheckrc'].global = false 3 | unused_args = false 4 | 5 | max_code_line_length = 120 6 | max_comment_line_length = false 7 | 8 | globals = { 9 | "sys", 10 | "go", 11 | "gui", 12 | "label", 13 | "render", 14 | "crash", 15 | "sprite", 16 | "sound", 17 | "tilemap", 18 | "spine", 19 | "particlefx", 20 | "physics", 21 | "factory", 22 | "collectionfactory", 23 | "iac", 24 | "msg", 25 | "vmath", 26 | "url", 27 | "http", 28 | "image", 29 | "json", 30 | "zlib", 31 | "iap", 32 | "push", 33 | "facebook", 34 | "hash", 35 | "hash_to_hex", 36 | "pprint", 37 | "init", 38 | "final", 39 | "update", 40 | "on_input", 41 | "on_message", 42 | "on_reload", 43 | "socket", 44 | "appsflyer", 45 | "custom_encoder", 46 | "table", 47 | "debug", 48 | "timer", 49 | "window", 50 | "unityads", 51 | "defos", 52 | "instantapp", 53 | "describe", 54 | "before", 55 | "after", 56 | "assert_true", 57 | "assert_equal", 58 | "assert_type", 59 | "test", 60 | "it", 61 | "context", 62 | "firebase", 63 | "defreview", 64 | "pb", 65 | "editor", 66 | "vibrate", 67 | "taptic_engine", 68 | "html5", 69 | "share", 70 | "gpgs", 71 | "clipboard", 72 | "gameanalytics", 73 | "lua_script_instance", 74 | "review", 75 | "safearea", 76 | "websocket", 77 | } 78 | -------------------------------------------------------------------------------- /resources/locale/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "game_title": "Template", 3 | "lang_name": "Русский", 4 | "lang_name_de": "Deutsch", 5 | "lang_name_en": "English", 6 | "lang_name_es": "Español", 7 | "lang_name_fr": "Français", 8 | "lang_name_it": "Italiano", 9 | "lang_name_ru": "Русский", 10 | "time_format_d": "%iд %02iч", 11 | "time_format_h": "%iч %02iм", 12 | "time_format_m": "%i:%02i", 13 | "ui_ads_desc": "Запуск рекламы...", 14 | "ui_agree": "Хорошо", 15 | "ui_cancel": "Отмена", 16 | "ui_completed": "Выполнено", 17 | "ui_exit": "Выход", 18 | "ui_header_confirm": "Подтверждение", 19 | "ui_header_info": "Информация", 20 | "ui_header_settings": "Настройки", 21 | "ui_label_off": "Выкл", 22 | "ui_label_on": "Вкл", 23 | "ui_language": "Язык", 24 | "ui_login": "Войти", 25 | "ui_menu": "Меню", 26 | "ui_music": "Музыка", 27 | "ui_no_ads": "БЕЗ РЕКЛАМЫ", 28 | "ui_notifications": "Уведомления", 29 | "ui_open": "Открыть", 30 | "ui_play": "Играть", 31 | "ui_rate_us": "Оценить", 32 | "ui_restore": "Восстановить", 33 | "ui_select": "Выберите", 34 | "ui_share": "Поделиться", 35 | "ui_social": "Сообщество", 36 | "ui_sound": "Звук", 37 | "ui_support_thanks": "Спасибо за поддержку!", 38 | "ui_time_days": "день", 39 | "ui_time_hours": "час", 40 | "ui_time_minutes": "мин", 41 | "ui_time_seconds": "сек", 42 | "ui_vibro": "Вибрация" 43 | } -------------------------------------------------------------------------------- /assets/scenes/scene_game.collection: -------------------------------------------------------------------------------- 1 | name: "scene_game" 2 | instances { 3 | id: "camera_game" 4 | prototype: "/game/system/camera.go" 5 | position { 6 | x: 694.0 7 | y: 647.0 8 | z: 0.0 9 | } 10 | rotation { 11 | x: 0.0 12 | y: 0.0 13 | z: 0.0 14 | w: 1.0 15 | } 16 | scale3 { 17 | x: 1.0 18 | y: 1.0 19 | z: 1.0 20 | } 21 | } 22 | scale_along_z: 0 23 | embedded_instances { 24 | id: "system" 25 | data: "" 26 | position { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | } 31 | rotation { 32 | x: 0.0 33 | y: 0.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | scale3 { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | } 42 | } 43 | embedded_instances { 44 | id: "gui" 45 | data: "components {\n" 46 | " id: \"scene_game\"\n" 47 | " component: \"/gui/scene_game/scene_game.gui\"\n" 48 | " position {\n" 49 | " x: 0.0\n" 50 | " y: 0.0\n" 51 | " z: 0.0\n" 52 | " }\n" 53 | " rotation {\n" 54 | " x: 0.0\n" 55 | " y: 0.0\n" 56 | " z: 0.0\n" 57 | " w: 1.0\n" 58 | " }\n" 59 | " property_decls {\n" 60 | " }\n" 61 | "}\n" 62 | "" 63 | position { 64 | x: 0.0 65 | y: 0.0 66 | z: 0.0 67 | } 68 | rotation { 69 | x: 0.0 70 | y: 0.0 71 | z: 0.0 72 | w: 1.0 73 | } 74 | scale3 { 75 | x: 1.0 76 | y: 1.0 77 | z: 1.0 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /game/const.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | -- Server compatability 4 | if not hash then 5 | hash = function(x) return x end 6 | end 7 | 8 | M.MAX_GUI_UPSCALE = 1.25 9 | M.SCENE_TRANSITION_TIME = 0.18 10 | 11 | M.PATH = { 12 | TRANSITION = "loader:/ui_scene_loader#ui_scene_loader", 13 | GUI = "scene_game:/gui" 14 | } 15 | 16 | M.MSG = { 17 | DESTROY_GO = hash("destroy_go"), 18 | LOADER_LOGO_SHOWED = hash("loader_logo_showed"), 19 | START_LOADING = hash("start_loading"), 20 | START_GAME = hash("start_game"), 21 | TRANSITION = hash("transition"), 22 | } 23 | 24 | M.WINDOW = { 25 | SCENE_MENU = "scene_menu", 26 | SCENE_GAME = "scene_game", 27 | WINDOW_INFO = "window_info", 28 | WINDOW_ADMIN = "window_admin", 29 | WINDOW_CONFIRM = "window_confirm", 30 | WINDOW_SETTINGS = "window_settings", 31 | } 32 | 33 | M.SOUND = { 34 | MUSIC_GAME = "music_game", 35 | MUSIC_MENU = "music_game", 36 | 37 | SFX_CLICK = "sfx_click", 38 | } 39 | 40 | M.RANDOM_SOUND_SPEED = function() 41 | return 1 + math.random() * 0.2 - 0.1 42 | end 43 | 44 | M.EVENT = { 45 | ---@class Event.MapTransition 46 | ---@field is_start boolean 47 | ---@field is_finish boolean 48 | MAP_TRANSITION = "map_transition", 49 | } 50 | 51 | M.SERVER_STORAGE = { 52 | USER_CODE_COLLECTION = "user_codes", 53 | USER_CODE_TOTAL_USERS_KEY = "total_users", 54 | USER_CODE_KEY = "user_code", 55 | } 56 | 57 | M.RENDER_ORDER = { 58 | TRANSITION = 13 59 | } 60 | 61 | return M 62 | -------------------------------------------------------------------------------- /eva/modules/migrations.lua: -------------------------------------------------------------------------------- 1 | --- Eva save migrations 2 | -- Can apply migrations to save 3 | -- @submodule eva 4 | 5 | 6 | local log = require("eva.log") 7 | local app = require("eva.app") 8 | 9 | local logger = log.get_logger("eva.migrations") 10 | 11 | local M = {} 12 | 13 | 14 | local function add(version, code) 15 | table.insert(app.migrations, code) 16 | assert(#app.migrations == version) 17 | end 18 | 19 | 20 | --- Add migration to the eva system 21 | -- Pass the migrations list in eva.init 22 | -- You should directly point the migration version 23 | -- in migration list (array from 1 to N) 24 | -- @function eva.migrations.set_migrations 25 | function M.set_migrations(migration_list) 26 | app.migrations = {} 27 | for i = 1, #migration_list do 28 | add(i, migration_list[i]) 29 | end 30 | end 31 | 32 | 33 | --- Return amount of migrations 34 | -- @function eva.migrations.get_count 35 | function M.get_count() 36 | return app.migrations and #app.migrations or 0 37 | end 38 | 39 | 40 | --- Apply the migrations 41 | -- @function eva.migrations.apply 42 | function M.apply(version, save_table) 43 | logger:info("Start apply migration to save", { version = version }) 44 | local migration_code = app.migrations[version] 45 | 46 | if not migration_code then 47 | logger:error("No migration with code", { version = version }) 48 | end 49 | 50 | migration_code(save_table) 51 | 52 | logger:info("Migration applied to save", { version = version }) 53 | end 54 | 55 | 56 | return M 57 | -------------------------------------------------------------------------------- /eva/modules/render/effects/default/default.material: -------------------------------------------------------------------------------- 1 | name: "default" 2 | tags: "default" 3 | vertex_program: "/eva/modules/render/effects/default/default.vp" 4 | fragment_program: "/eva/modules/render/effects/default/default.fp" 5 | vertex_space: VERTEX_SPACE_WORLD 6 | vertex_constants { 7 | name: "view_proj" 8 | type: CONSTANT_TYPE_VIEWPROJ 9 | value { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 0.0 14 | } 15 | } 16 | fragment_constants { 17 | name: "tint" 18 | type: CONSTANT_TYPE_USER 19 | value { 20 | x: 1.0 21 | y: 1.0 22 | z: 1.0 23 | w: 1.0 24 | } 25 | } 26 | fragment_constants { 27 | name: "resolution" 28 | type: CONSTANT_TYPE_USER 29 | value { 30 | x: 900.0 31 | y: 1600.0 32 | z: 0.0 33 | w: 0.0 34 | } 35 | } 36 | fragment_constants { 37 | name: "effects" 38 | type: CONSTANT_TYPE_USER 39 | value { 40 | x: 0.0 41 | y: 0.0 42 | z: 0.0 43 | w: 0.0 44 | } 45 | } 46 | fragment_constants { 47 | name: "effects2" 48 | type: CONSTANT_TYPE_USER 49 | value { 50 | x: 0.0 51 | y: 0.0 52 | z: 0.0 53 | w: 0.0 54 | } 55 | } 56 | fragment_constants { 57 | name: "effects3" 58 | type: CONSTANT_TYPE_USER 59 | value { 60 | x: 0.0 61 | y: 0.0 62 | z: 0.0 63 | w: 0.0 64 | } 65 | } 66 | samplers { 67 | name: "original" 68 | wrap_u: WRAP_MODE_REPEAT 69 | wrap_v: WRAP_MODE_REPEAT 70 | filter_min: FILTER_MODE_MIN_NEAREST 71 | filter_mag: FILTER_MODE_MAG_NEAREST 72 | } 73 | -------------------------------------------------------------------------------- /gui/components/window_shadow.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | textures { 3 | name: "window_general" 4 | texture: "/assets/atlases/window_general.atlas" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 450.0 15 | y: 800.0 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 900.0 33 | y: 1600.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 0.14901961 39 | y: 0.16862746 40 | z: 0.26666668 41 | w: 1.0 42 | } 43 | type: TYPE_BOX 44 | blend_mode: BLEND_MODE_ALPHA 45 | texture: "window_general/pixel" 46 | id: "root" 47 | xanchor: XANCHOR_NONE 48 | yanchor: YANCHOR_NONE 49 | pivot: PIVOT_CENTER 50 | adjust_mode: ADJUST_MODE_STRETCH 51 | layer: "general" 52 | inherit_alpha: true 53 | slice9 { 54 | x: 0.0 55 | y: 0.0 56 | z: 0.0 57 | w: 0.0 58 | } 59 | clipping_mode: CLIPPING_MODE_NONE 60 | clipping_visible: true 61 | clipping_inverted: false 62 | alpha: 0.8 63 | template_node_child: false 64 | size_mode: SIZE_MODE_MANUAL 65 | custom_type: 0 66 | enabled: true 67 | visible: true 68 | } 69 | layers { 70 | name: "general" 71 | } 72 | material: "/builtins/materials/gui.material" 73 | adjust_reference: ADJUST_REFERENCE_PARENT 74 | max_nodes: 512 75 | -------------------------------------------------------------------------------- /eva/system/eva.texture_profiles: -------------------------------------------------------------------------------- 1 | path_settings { 2 | path: "**" 3 | profile: "RGBA_32bit" 4 | } 5 | profiles { 6 | name: "RGBA_32bit" 7 | platforms { 8 | os: OS_ID_WINDOWS 9 | formats { 10 | format: TEXTURE_FORMAT_RGBA 11 | compression_level: BEST 12 | compression_type: COMPRESSION_TYPE_DEFAULT 13 | } 14 | mipmaps: false 15 | max_texture_size: 0 16 | premultiply_alpha: true 17 | } 18 | platforms { 19 | os: OS_ID_IOS 20 | formats { 21 | format: TEXTURE_FORMAT_RGBA 22 | compression_level: BEST 23 | compression_type: COMPRESSION_TYPE_DEFAULT 24 | } 25 | mipmaps: false 26 | max_texture_size: 0 27 | premultiply_alpha: true 28 | } 29 | platforms { 30 | os: OS_ID_ANDROID 31 | formats { 32 | format: TEXTURE_FORMAT_RGBA 33 | compression_level: BEST 34 | compression_type: COMPRESSION_TYPE_DEFAULT 35 | } 36 | mipmaps: false 37 | max_texture_size: 0 38 | premultiply_alpha: true 39 | } 40 | platforms { 41 | os: OS_ID_WEB 42 | formats { 43 | format: TEXTURE_FORMAT_RGBA 44 | compression_level: BEST 45 | compression_type: COMPRESSION_TYPE_DEFAULT 46 | } 47 | mipmaps: false 48 | max_texture_size: 0 49 | premultiply_alpha: true 50 | } 51 | platforms { 52 | os: OS_ID_OSX 53 | formats { 54 | format: TEXTURE_FORMAT_RGBA 55 | compression_level: BEST 56 | compression_type: COMPRESSION_TYPE_DEFAULT 57 | } 58 | mipmaps: false 59 | max_texture_size: 0 60 | premultiply_alpha: true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /gui/ui_scene_transition/ui_scene_transition.lua: -------------------------------------------------------------------------------- 1 | --- For component interest functions 2 | --- see https://github.com/Insality/druid/blob/develop/docs_md/02-creating_custom_components.md 3 | --- Require this component in you gui file: 4 | --- local UiSceneLoader = require("gui.ui_scene_transition.ui_scene_transition") 5 | --- And create this component via: 6 | --- self.ui_scene_transition = self.druid:new(UiSceneLoader, template, nodes) 7 | 8 | local luax = require("eva.luax") 9 | local const = require("game.const") 10 | local component = require("druid.component") 11 | 12 | ---@class ui_scene_transition: druid.base_component 13 | ---@field root node 14 | ---@field druid druid_instance 15 | local UiSceneLoader = component.create("ui_scene_transition") 16 | 17 | local SCHEME = { 18 | ROOT = "root", 19 | BACKGROUND = "background", 20 | } 21 | 22 | 23 | ---@param template string 24 | ---@param nodes table 25 | function UiSceneLoader:init(template, nodes) 26 | self:set_template(template) 27 | self:set_nodes(nodes) 28 | self.druid = self:get_druid() 29 | 30 | self.root = self:get_node(SCHEME.ROOT) 31 | gui.set_enabled(self.root, false) 32 | end 33 | 34 | 35 | function UiSceneLoader:set_loading(is_loading, callback) 36 | gui.set_enabled(self.root, true) 37 | 38 | local time = const.SCENE_TRANSITION_TIME 39 | gui.animate(self.root, luax.gui.PROP_ALPHA, is_loading and 1 or 0, gui.EASING_OUTSINE, time, 0, function() 40 | if not is_loading then 41 | gui.set_enabled(self.root, false) 42 | end 43 | if callback then 44 | callback() 45 | end 46 | end) 47 | end 48 | 49 | 50 | return UiSceneLoader 51 | -------------------------------------------------------------------------------- /gui/scene_game/scene_game.gui_script: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local luax = require("eva.luax") 3 | local druid = require("druid.druid") 4 | local const = require("game.const") 5 | 6 | ---@class scene.game 7 | 8 | 9 | ---@param self scene.game 10 | local function on_button_menu(self) 11 | eva.window.show(const.WINDOW.WINDOW_SETTINGS, { 12 | callbacks = { 13 | on_exit = function() 14 | 15 | ---@type window.confirm_data 16 | local window_confirm_data = {} 17 | window_confirm_data.text = "Sure exit from game?" 18 | window_confirm_data.callbacks = { 19 | on_agree = function() 20 | eva.window.show_scene(const.WINDOW.SCENE_MENU) 21 | end 22 | } 23 | eva.window.show(const.WINDOW.WINDOW_CONFIRM, window_confirm_data) 24 | end 25 | } 26 | }) 27 | end 28 | 29 | 30 | ---@param self scene.game 31 | function init(self) 32 | self.druid = druid.new(self) 33 | 34 | self.button_menu = self.druid:new_button("button_menu/button", on_button_menu) 35 | 36 | local insets = safearea.get_insets() 37 | luax.gui.add_y(gui.get_node("NW_Anchor"), -insets.top / 2) 38 | end 39 | 40 | 41 | ---@param self scene.game 42 | function final(self) 43 | self.druid:final() 44 | end 45 | 46 | 47 | ---@param self scene.game 48 | function update(self, dt) 49 | self.druid:update(dt) 50 | end 51 | 52 | 53 | ---@param self scene.game 54 | function on_message(self, message_id, message, sender) 55 | self.druid:on_message(message_id, message, sender) 56 | end 57 | 58 | 59 | ---@param self scene.game 60 | function on_input(self, action_id, action) 61 | return self.druid:on_input(action_id, action) 62 | end 63 | -------------------------------------------------------------------------------- /eva/modules/vibrate.lua: -------------------------------------------------------------------------------- 1 | --- Eva vibrate module 2 | -- It uses two different native extensions to unify vibrate API on 3 | -- Android and iOS. 4 | -- For use it you should add dependencies for vibrate for Android and iOS (see dependencies.txt, 5 | -- vibrate and taptic_engine) 6 | -- @submodule eva 7 | 8 | local const = require("eva.const") 9 | 10 | local device = require("eva.modules.device") 11 | local storage = require("eva.modules.storage") 12 | 13 | 14 | local M = {} 15 | 16 | 17 | --- Make phone vibrate 18 | -- @function eva.vibrate.vibrate 19 | -- @tparam number vibrate_pattern Vibrate const.VIBRATE 20 | function M.vibrate(vibrate_pattern) 21 | if not storage.get(const.STORAGE.VIBRATE_IS_ENABLED) then 22 | return 23 | end 24 | 25 | if device.is_android() and vibrate then 26 | vibrate.vibrate(vibrate_pattern.Android) 27 | end 28 | 29 | if device.is_ios() and taptic_engine and taptic_engine.isSupported() then 30 | -- TODO: Get the iOS device with taptic engine for tests 31 | taptic_engine.impact(vibrate_pattern.iOS) 32 | end 33 | end 34 | 35 | 36 | --- Return if vibrate is enabled for user 37 | -- @function eva.vibrate.is_enabled 38 | -- @treturn boolean|nil Is vibrate enabled 39 | function M.is_enabled() 40 | return storage.get(const.STORAGE.VIBRATE_IS_ENABLED) 41 | end 42 | 43 | 44 | --- Turn on or off vibrate for user 45 | -- @function eva.vibrate.set_enabled 46 | -- @tparam boolean is_enabled Vibrate state 47 | function M.set_enabled(is_enabled) 48 | storage.set(const.STORAGE.VIBRATE_IS_ENABLED, is_enabled) 49 | end 50 | 51 | 52 | function M.after_eva_init() 53 | if M.is_enabled() == nil then 54 | M.set_enabled(true) 55 | end 56 | end 57 | 58 | 59 | return M 60 | -------------------------------------------------------------------------------- /game/profile/Profile.lua: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local app = require("eva.app") 3 | local class = require("eva.libs.middleclass") 4 | local log = require("eva.log") 5 | local Event = require("eva.event") 6 | 7 | local Tutorial = require("game.profile.Tutorial") 8 | 9 | local logger = log.get_logger("profile") 10 | 11 | ---@class Profile 12 | ---@field data game.ProfileData 13 | local Profile = class("game.profile") 14 | 15 | 16 | function Profile:initialize() 17 | ---@type game.ProfileData 18 | self.data = eva.proto.get("game.ProfileData") 19 | eva.saver.add_save_part("game.ProfileData", self.data) 20 | 21 | self.tutorial = Tutorial(self.data.tutorial_data) 22 | 23 | self.on_user_code_change = Event() 24 | end 25 | 26 | 27 | ---@return game.ProfileData 28 | function Profile:get_profile() 29 | return self.data 30 | end 31 | 32 | 33 | ---@return string 34 | function Profile:get_user_id() 35 | return self.data.user_id 36 | end 37 | 38 | 39 | ---@return string 40 | function Profile:get_user_code() 41 | return self.data.user_code 42 | end 43 | 44 | 45 | ---@param user_id string 46 | function Profile:set_user_id(user_id) 47 | if self.data.user_id ~= user_id then 48 | self.data.user_id = user_id 49 | logger:info("Set profile user_id", { user_id = user_id }) 50 | end 51 | app.analytics:set_user_id(user_id) 52 | end 53 | 54 | 55 | ---@param user_code string 56 | function Profile:set_user_code(user_code) 57 | if user_code and self.data.user_code ~= user_code then 58 | self.data.user_code = user_code 59 | logger:info("Set profile user_code", { user_code = user_code }) 60 | self.on_user_code_change:trigger(user_code) 61 | end 62 | end 63 | 64 | 65 | return Profile 66 | -------------------------------------------------------------------------------- /eva/modules/storage.lua: -------------------------------------------------------------------------------- 1 | --- Eva key-value storage 2 | -- Using for simple key-value local storage 3 | -- @submodule eva 4 | 5 | local app = require("eva.app") 6 | local luax = require("eva.luax") 7 | local const = require("eva.const") 8 | local log = require("eva.log") 9 | 10 | local proto = require("eva.modules.proto") 11 | local saver = require("eva.modules.saver") 12 | 13 | local logger = log.get_logger("eva.storage") 14 | 15 | local M = {} 16 | 17 | 18 | --- Get the value from the storage. 19 | -- @function eva.storage.get 20 | -- @tparam string id The record id 21 | function M.get(id, defalt_value) 22 | local storage = app[const.EVA.STORAGE].storage 23 | local value = storage[id] 24 | 25 | if not value then 26 | return defalt_value 27 | end 28 | 29 | return value.s_value or value.i_value or value.b_value 30 | end 31 | 32 | --- Set the value to eva storage 33 | -- @function eva.storage.set 34 | -- @tparam string id The record id 35 | -- @tparam string|number|bool value Value 36 | function M.set(id, value) 37 | local storage = app[const.EVA.STORAGE].storage 38 | local v = proto.get(const.EVA.STORAGE_VALUE) 39 | 40 | if type(value) == const.TYPE.STRING then 41 | v.s_value = value 42 | end 43 | if type(value) == const.TYPE.NUMBER then 44 | v.i_value = value 45 | end 46 | if type(value) == const.TYPE.BOOLEAN then 47 | v.b_value = value 48 | end 49 | 50 | if luax.table.is_empty(v) then 51 | logger:error("Wrong args type in eva.storage", { id = id, value = value }) 52 | return 53 | end 54 | 55 | storage[id] = v 56 | end 57 | 58 | 59 | function M.on_eva_init() 60 | app[const.EVA.STORAGE] = proto.get(const.EVA.STORAGE) 61 | saver.add_save_part(const.EVA.STORAGE, app[const.EVA.STORAGE]) 62 | end 63 | 64 | 65 | return M 66 | -------------------------------------------------------------------------------- /eva/modules/input/input_pinch.lua: -------------------------------------------------------------------------------- 1 | -- Input pinch logic 2 | 3 | 4 | local app = require("eva.app") 5 | local luax = require("eva.luax") 6 | local const = require("eva.const") 7 | 8 | local M = {} 9 | 10 | 11 | local function get_distance(action) 12 | if not action.touch then 13 | return 0 14 | end 15 | 16 | local t1 = action.touch[1] 17 | local t2 = action.touch[2] 18 | 19 | return luax.math.distance(t1.screen_x, t1.screen_y, t2.screen_x, t2.screen_y) 20 | end 21 | 22 | 23 | local function handle_pinch(data, state, action) 24 | -- Zooming start 25 | local settings = app.settings.input 26 | state.input_type = const.INPUT_TYPE.PINCH 27 | 28 | if not state.is_pinch then 29 | state.input_type = const.INPUT_TYPE.PINCH_START 30 | state.is_pinch = true 31 | state.pinch_distance = data.dist 32 | state.pinch_pos = data.center 33 | end 34 | 35 | -- Pinch delta 36 | state.pinch_delta = (data.dist - state.pinch_distance) / (1000 * settings.zoom_speed) 37 | state.pinch_distance = data.dist 38 | state.pinch_pos = data.center 39 | end 40 | 41 | 42 | function M.handle_pinch(state, action_id, action) 43 | local g = app.input.gesture.on_input(action_id, action) 44 | 45 | -- Handle pinch 46 | if g and g.two_finger and g.two_finger.pinch then 47 | g.two_finger.pinch.dist = get_distance(action) 48 | handle_pinch(g.two_finger.pinch, state) 49 | end 50 | 51 | -- Detect end pinch state 52 | if action.touch and state.is_pinch then 53 | local no_touch = #action.touch ~= 2 54 | 55 | local all_released = true 56 | for i = 1, #action.touch do 57 | if not action.touch[i].released then 58 | all_released = false 59 | end 60 | end 61 | 62 | if no_touch or all_released then 63 | state.is_pinch = false 64 | state.input_type = const.INPUT_TYPE.PINCH_END 65 | end 66 | end 67 | end 68 | 69 | 70 | return M 71 | -------------------------------------------------------------------------------- /eva/libs/astar/basic_handler.lua: -------------------------------------------------------------------------------- 1 | --- Basic astar handler for reuse 2 | 3 | local luax = require("eva.luax") 4 | local node = require("eva.libs.astar.node") 5 | 6 | local M = {} 7 | 8 | 9 | function M.locations_are_equal(a, b) 10 | if not b then 11 | return false 12 | end 13 | return a.x == b.x and a.y == b.y 14 | end 15 | 16 | 17 | function M.get_score(from_node, to_node, dest_node) 18 | return luax.math.manhattan(from_node.x, from_node.y, to_node.x, to_node.y) 19 | end 20 | 21 | 22 | function M.handle_node(map_handler, from_node, to_node, dest_node) 23 | if to_node and to_node.move_cost then 24 | local em_cost = map_handler.get_score(from_node, to_node, dest_node) 25 | 26 | to_node.move_cost = to_node.move_cost + from_node.move_cost 27 | to_node.score = to_node.move_cost + em_cost 28 | to_node.parent = from_node 29 | 30 | return to_node 31 | end 32 | 33 | return nil 34 | end 35 | 36 | 37 | function M.get_adjacent_nodes(map_handler, from_node, dest_node) 38 | local nodes = {} 39 | local x, y = from_node.x, from_node.y 40 | 41 | local neighbors = map_handler.get_neighbors(map_handler, x, y) 42 | 43 | for i = 1, #neighbors do 44 | local offset = neighbors[i] 45 | local to_node = map_handler.get_node(x + offset[1], y + offset[2]) 46 | local n = M.handle_node(map_handler, from_node, to_node, dest_node) 47 | if n then 48 | table.insert(nodes, n) 49 | end 50 | end 51 | 52 | return nodes 53 | end 54 | 55 | 56 | function M.get_neighbors(map_handler, x, y) 57 | return map_handler.neighbors or {} 58 | end 59 | 60 | 61 | function M.get_handler(get_node_fn) 62 | return { 63 | get_node = function(x, y) 64 | local cost = get_node_fn(x, y) 65 | return node.get(x, y, cost) 66 | end, 67 | get_cost = function(x, y) 68 | return get_node_fn(x, y) 69 | end, 70 | neighbors = {} 71 | } 72 | end 73 | 74 | 75 | return M 76 | -------------------------------------------------------------------------------- /gui/window_template/window_template.gui_script: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") 2 | local druid = require("druid.druid") 3 | 4 | local const = require("game.const") 5 | 6 | local WINDOW_ID = const.WINDOW.WINDOW_SETTINGS 7 | 8 | ---@class window.template 9 | 10 | ---@param self window.template 11 | ---@param callback function 12 | local function close_window(self, callback) 13 | local callbacks = self.data.callbacks 14 | 15 | eva.window.disappear(WINDOW_ID, function() 16 | if callbacks and callbacks.on_close then 17 | callbacks.on_close() 18 | end 19 | if callback then 20 | callback() 21 | end 22 | end) 23 | end 24 | 25 | 26 | ---@param self window.template 27 | function init(self) 28 | self.druid = druid.new(self) 29 | self.root = gui.get_node("root") 30 | self.data = eva.window.get_data(WINDOW_ID) 31 | 32 | self.druid:new_button("window_shadow/root", close_window) 33 | self.druid:new_back_handler(close_window) 34 | self.druid:new_blocker("window_background") 35 | self.druid:new_button("button_close/button", close_window) 36 | self.druid:new_lang_text("base_window/text_header", "ui_header_id") 37 | 38 | self.druid:new_layout("node_upscale_limit"):set_max_gui_upscale(const.MAX_GUI_UPSCALE) 39 | 40 | eva.window.appear(WINDOW_ID) 41 | end 42 | 43 | 44 | ---@param self window.template 45 | function final(self) 46 | self.druid:final() 47 | end 48 | 49 | 50 | ---@param self window.template 51 | function update(self, dt) 52 | self.druid:update(dt) 53 | end 54 | 55 | 56 | ---@param self window.template 57 | function on_message(self, message_id, message, sender) 58 | eva.window.on_message(WINDOW_ID, message_id, message, sender) 59 | self.druid:on_message(message_id, message, sender) 60 | end 61 | 62 | 63 | ---@param self window.template 64 | function on_input(self, action_id, action) 65 | return self.druid:on_input(action_id, action) 66 | end 67 | -------------------------------------------------------------------------------- /eva/modules/queue.lua: -------------------------------------------------------------------------------- 1 | --- Eva queue module. 2 | -- Queue have logic to perform some 3 | -- sequence actions or other stuff with 4 | -- queue logic. 5 | -- @submodule eva 6 | 7 | local app = require("eva.app") 8 | local const = require("eva.const") 9 | local log = require("eva.log") 10 | 11 | local saver = require("eva.modules.saver") 12 | local proto = require("eva.modules.proto") 13 | 14 | local logger = log.get_logger("eva.queue") 15 | 16 | local M = {} 17 | 18 | 19 | function M.create_params(queue_callback, delay, check_function, is_storage_data) 20 | local params = {} 21 | params.queue_callback = queue_callback 22 | params.delay = delay 23 | params.check_function = check_function 24 | params.is_storage_data = is_storage_data 25 | 26 | return params 27 | end 28 | 29 | 30 | function M.create_queue(params) 31 | local queue = {} 32 | queue.params = params 33 | queue.id = "" 34 | queue.queue = {} 35 | queue.is_pause = false 36 | queue.timer = 0 37 | 38 | return queue 39 | end 40 | 41 | 42 | function M.delete_queue(queue_id) 43 | end 44 | 45 | 46 | function M.add_task(queue_id, task_params) 47 | local task_id = "" 48 | return task_id 49 | end 50 | 51 | 52 | function M.remove_task(queue_id, task_id) 53 | end 54 | 55 | 56 | function M.get_task(queue_id, task_id) 57 | end 58 | 59 | 60 | function M.get_tasks(queue_id) 61 | end 62 | 63 | 64 | function M.get_first_task(queue_id) 65 | end 66 | 67 | 68 | function M.get_last_task(queue_id) 69 | end 70 | 71 | 72 | function M.is_empty(queue_id) 73 | end 74 | 75 | 76 | function M.get_task_count(queue_id) 77 | end 78 | 79 | 80 | function M.check_queue(queue_id) 81 | end 82 | 83 | 84 | function M.on_eva_init() 85 | --app[const.EVA.QUEUE] = proto.get(const.EVA.QUEUE) 86 | --saver.add_save_part(const.EVA.QUEUE, app[const.EVA.QUEUE]) 87 | end 88 | 89 | 90 | function M.on_eva_update(dt) 91 | end 92 | 93 | 94 | return M 95 | -------------------------------------------------------------------------------- /eva/modules/iaps/iaps_yandex.lua: -------------------------------------------------------------------------------- 1 | local log = require("eva.log") 2 | local app = require("eva.app") 3 | local const = require("eva.const") 4 | local yagames = const.require("yagames.yagames") 5 | 6 | local logger = log.get_logger("iap_yandex") 7 | 8 | local Iaps = {} 9 | 10 | 11 | function Iaps.init() 12 | yagames.payments_init({ signed = true }, function(_, err) 13 | if err then 14 | logger:error("Yandex IAP init error", { error = err }) 15 | end 16 | end) 17 | end 18 | 19 | 20 | function Iaps.buy(iap_id) 21 | yagames.payments_purchase({ id = iap_id }, function(_, err, purchase) 22 | if err then 23 | logger:error("Yandex IAP buy error", { error = err }) 24 | end 25 | end) 26 | end 27 | 28 | 29 | function Iaps.consume(transaction_token) 30 | yagames.payments_consume_purchase(transaction_token, function() 31 | logger:error("Yandex consume callback") 32 | end) 33 | end 34 | 35 | 36 | function Iaps.fetch_products() 37 | yagames.payments_get_catalog(function(_, err, catalog) 38 | if err then 39 | logger:error("Yandex IAP fetch error", { error = err }) 40 | end 41 | logger:info("Fetch IAP products", { catalog = catalog }) 42 | 43 | for index = 1, #catalog do 44 | local product = catalog[index] 45 | 46 | local iap_info = app.iap_products[product.id] 47 | iap_info.is_available = true 48 | iap_info.currency_code = product.priceCurrencyCode 49 | iap_info.title = product.title 50 | iap_info.description = product.description 51 | iap_info.price_string = product.price 52 | iap_info.price = product.priceValue 53 | end 54 | end) 55 | end 56 | 57 | 58 | function Iaps.restore() 59 | yagames.payments_get_purchases(function(_, err, purchases) 60 | if err then 61 | logger:error("Yandex IAP restore error", { error = err }) 62 | end 63 | logger:info("Restore purchases", { purchases = purchases }) 64 | end) 65 | end 66 | 67 | 68 | return Iaps 69 | -------------------------------------------------------------------------------- /eva/resources/window_settings_example.lua: -------------------------------------------------------------------------------- 1 | -- Window settings example 2 | -- default settings will be extend with windows custom 3 | -- every settings should have next field: 4 | -- - render_order - number 0..15 to gui.set_render_order 5 | -- - appear_func - function(cb) on window show 6 | -- - disappear_func - function(cb) on window close 7 | -- - before_show_scene - function before show scene 8 | -- - after_show_scene - function after show scene 9 | -- - before_show_window - function before show window 10 | -- - after_show_window - function after show window 11 | -- callback should be executed always! 12 | -- @local 13 | 14 | 15 | local function appear_simple(settings, cb) 16 | local root = gui.get_node("root") 17 | gui.set_scale(root, vmath.vector3(0.01)) 18 | gui.animate(root, gui.PROP_SCALE, 1, gui.EASING_OUTSINE, 0.4, 0, cb) 19 | end 20 | 21 | 22 | local function disappear_simple(settings, cb) 23 | local root = gui.get_node("root") 24 | gui.animate(root, gui.PROP_SCALE, 0.01, gui.EASING_OUTSINE, 0.4, 0, cb) 25 | end 26 | 27 | 28 | local function before_show_scene(cb) 29 | cb() 30 | end 31 | 32 | 33 | local function after_show_scene(cb) 34 | if cb then 35 | cb() 36 | end 37 | end 38 | 39 | 40 | local function before_show_window(cb) 41 | cb() 42 | end 43 | 44 | 45 | local function after_show_window(cb) 46 | if cb then 47 | cb() 48 | end 49 | end 50 | 51 | 52 | local M = { 53 | ["default"] = { 54 | render_order = 10, 55 | appear_func = appear_simple, 56 | disappear_func = disappear_simple, 57 | before_show_scene = before_show_scene, 58 | after_show_scene = after_show_scene, 59 | before_show_window = before_show_window, 60 | after_show_window = after_show_window, 61 | is_popup_on_popup = false, 62 | 63 | test_value = "test", 64 | }, 65 | 66 | ["window_test"] = { 67 | render_order = 11 68 | }, 69 | 70 | ["window_popup"] = { 71 | render_order = 13, 72 | is_popup_on_popup = true, 73 | } 74 | } 75 | 76 | return M 77 | -------------------------------------------------------------------------------- /eva/modules/callbacks.lua: -------------------------------------------------------------------------------- 1 | --- Eva callbacks system module. 2 | -- Using for pass callbacks though context 3 | -- It make wrap callbacks to call it via messages by callback index 4 | -- @submodule eva 5 | 6 | 7 | local app = require("eva.app") 8 | 9 | local M = {} 10 | 11 | 12 | --- Wrap callback 13 | -- It return index for callback, You can call it now 14 | -- via eva.callbacks.call(index, ...) 15 | -- @function eva.callbacks.create 16 | -- @tparam function callback Callback to wrap 17 | -- @treturn number index New index of wrapped callback 18 | function M.create(callback) 19 | local data = app.callbacks_data 20 | data.last_index = data.last_index + 1 21 | data.callbacks[data.last_index] = { 22 | context = lua_script_instance.Get(), 23 | callback = callback 24 | } 25 | 26 | return data.last_index 27 | end 28 | 29 | 30 | --- Call wrapped callback 31 | -- @function eva.callbacks.call 32 | -- @tparam number index Index of wrapped callback 33 | -- @tparam args ... Args of calling callback 34 | function M.call(index, ...) 35 | local data = app.callbacks_data 36 | 37 | if data.callbacks[index] then 38 | local callback_data = data.callbacks[index] 39 | -- data.callbacks[index] = nil 40 | 41 | local current_context = lua_script_instance.Get() 42 | 43 | lua_script_instance.Set(callback_data.context) 44 | local ok, result = pcall(callback_data.callback, ...) 45 | lua_script_instance.Set(current_context) 46 | 47 | if not ok then 48 | error(result) 49 | end 50 | 51 | return result 52 | end 53 | 54 | return false 55 | end 56 | 57 | 58 | --- Clear callback 59 | -- @function eva.callbacks.clear 60 | -- @tparam number index Index of wrapped callback 61 | function M.clear(index) 62 | local data = app.callbacks_data 63 | if data.callbacks[index] then 64 | data.callbacks[index] = nil 65 | return true 66 | end 67 | 68 | return false 69 | end 70 | 71 | 72 | function M.before_eva_init() 73 | app.callbacks_data = { 74 | callbacks = {}, 75 | last_index = 0 76 | } 77 | end 78 | 79 | 80 | return M 81 | -------------------------------------------------------------------------------- /server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | cockroachdb: 4 | image: cockroachdb/cockroach:latest-v20.2 5 | command: start-single-node --insecure --store=attrs=ssd,path=/var/lib/cockroach/ 6 | restart: "no" 7 | volumes: 8 | - data:/var/lib/cockroach 9 | expose: 10 | - "8080" 11 | - "26257" 12 | ports: 13 | - "26257:26257" 14 | - "8080:8080" 15 | healthcheck: 16 | test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"] 17 | interval: 3s 18 | timeout: 3s 19 | retries: 5 20 | nakama: 21 | image: registry.heroiclabs.com/heroiclabs/nakama:3.16.0 22 | entrypoint: 23 | - "/bin/sh" 24 | - "-ecx" 25 | - > 26 | /nakama/nakama migrate up --database.address "root@cockroachdb:26257" && 27 | exec /nakama/nakama --name Template --database.address "root@cockroachdb:26257" \ 28 | --logger.level DEBUG --session.token_expiry_sec 10800 \ 29 | --socket.server_key SERVER_KER_HERE --runtime.http_key HTTP_KEY_HERE \ 30 | --session.encryption_key ENCRYPTION_KEY_HERE --session.refresh_encryption_key REFRESH_KEY_HERE \ 31 | --console.username USERNAME --console.password PASSWORD --console.port 7569 \ 32 | --console.signing_key SIGN_KEY --matchmaker.interval_sec 10 --shutdown_grace_sec 360 33 | restart: "no" 34 | volumes: 35 | - ./:/nakama/data 36 | - ../game/shared:/nakama/data/modules/game/shared 37 | - ../game/const.lua:/nakama/data/modules/game/const.lua 38 | links: 39 | - "cockroachdb:db" 40 | depends_on: 41 | cockroachdb: 42 | condition: service_healthy 43 | expose: 44 | - "7349" 45 | - "7350" 46 | - "7569" 47 | - "9100" 48 | ports: 49 | - "7349:7349" 50 | - "7350:7350" 51 | - "7569:7569" 52 | healthcheck: 53 | test: ["CMD", "curl", "-f", "http://localhost:7350/"] 54 | interval: 10s 55 | timeout: 5s 56 | retries: 5 57 | volumes: 58 | data: 59 | -------------------------------------------------------------------------------- /game/analytics/Analytics.lua: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local evaconst = require("eva.const") 3 | local class = require("eva.libs.middleclass") 4 | 5 | local const = require("game.const") 6 | 7 | local GameAnalytics = require("game.analytics.analytics_gameanalytics") 8 | 9 | 10 | ---@class Analytics 11 | local Analytics = class("game.Analytics") 12 | 13 | 14 | function Analytics:initialize() 15 | eva.events.subscribe(evaconst.EVENT.ADS_SUCCESS, self._on_ads_success, self) 16 | eva.events.subscribe(evaconst.EVENT.ADS_ERROR, self._on_ads_error, self) 17 | eva.events.subscribe(evaconst.EVENT.TOKEN_CHANGE, self._on_token_change, self) 18 | 19 | self._adapters = {} 20 | if gameanalytics then 21 | table.insert(self._adapters, GameAnalytics) 22 | end 23 | 24 | self:_call("init") 25 | end 26 | 27 | 28 | function Analytics:final() 29 | end 30 | 31 | 32 | ---@param user_id string 33 | function Analytics:set_user_id(user_id) 34 | self:_call("set_user_id", user_id) 35 | end 36 | 37 | 38 | function Analytics:_on_ads_success(params) 39 | self:_call("_on_ads_success", params) 40 | end 41 | 42 | 43 | function Analytics:_on_ads_error(params) 44 | self:_call("_on_ads_error", params) 45 | end 46 | 47 | 48 | function Analytics:_on_ads_reward(params) 49 | self:_call("_on_ads_reward", params) 50 | end 51 | 52 | 53 | function Analytics:_on_token_change(params) 54 | if params.container_id ~= evaconst.WALLET_CONTAINER then 55 | return 56 | end 57 | 58 | self:_call("_on_token_change", params) 59 | end 60 | 61 | 62 | function Analytics:_on_game_start_or_complete(params) 63 | self:_call("_on_game_start_or_complete", params) 64 | end 65 | 66 | 67 | function Analytics:_on_content_get(params) 68 | self:_call("_on_content_get", params) 69 | end 70 | 71 | 72 | function Analytics:_on_duel_invite(params) 73 | self:_call("_on_duel_invite", params) 74 | end 75 | 76 | 77 | function Analytics:_call(event, params) 78 | eva.log:debug("Analytics event", { event = event, params = params }) 79 | 80 | for index = 1, #self._adapters do 81 | if self._adapters[index][event] then 82 | self._adapters[index][event](params) 83 | end 84 | end 85 | end 86 | 87 | 88 | return Analytics 89 | -------------------------------------------------------------------------------- /eva/modules/errors.lua: -------------------------------------------------------------------------------- 1 | --- Eva error module. 2 | -- Handle and store all lua errors in the game 3 | -- Can send error to the server, pointed in eva 4 | -- settings.json 5 | -- @submodule eva 6 | 7 | 8 | local app = require("eva.app") 9 | local log = require("eva.log") 10 | local luax = require("eva.luax") 11 | 12 | local game = require("eva.modules.game") 13 | local device = require("eva.modules.device") 14 | 15 | local logger = log.get_logger("eva.errors") 16 | 17 | local M = {} 18 | 19 | 20 | local send_traceback = function(message, traceback) 21 | local errors_sended = app.errors_sended 22 | local is_sended = luax.table.contains(errors_sended, message) 23 | 24 | if not is_sended then 25 | local info = sys.get_sys_info() 26 | 27 | traceback = string.gsub(traceback, "\n", " ") 28 | traceback = string.gsub(traceback, "\t", " ") 29 | logger:fatal("Error in lua module", { 30 | error_message = message, 31 | traceback = traceback, 32 | version = sys.get_config("project.version"), 33 | id = device.get_device_id(), 34 | sys_name = info.system_name, 35 | sys_version = info.system_version, 36 | model = info.device_model, 37 | manufacturer = info.manufacturer 38 | }) 39 | table.insert(errors_sended, message) 40 | end 41 | end 42 | 43 | 44 | local function check_crash() 45 | local crash_previous = crash.load_previous() 46 | if crash_previous == nil then 47 | return 48 | end 49 | 50 | print("Crash [" .. crash.get_sys_field(crash_previous, crash.SYSFIELD_ENGINE_VERSION) .. "] [" .. crash.get_sys_field(crash_previous, crash.SYSFIELD_ENGINE_HASH) .. "]") 51 | print("Signum [" .. crash.get_signum(crash_previous) .. "]") 52 | print("Userdata0 = [" .. crash.get_user_field(crash_previous, 0) .. "]") 53 | print("Pretty Backtrace:\n" .. crash.get_extra_data(crash_previous)) 54 | 55 | crash.release(crash_previous) 56 | end 57 | 58 | 59 | function M.before_eva_init() 60 | check_crash() 61 | end 62 | 63 | 64 | function M.on_eva_init() 65 | app.errors_sended = {} 66 | 67 | if game.is_debug() and not device.is_web() then 68 | return 69 | end 70 | 71 | sys.set_error_handler(function(source, message, traceback) 72 | send_traceback(message, traceback) 73 | end) 74 | end 75 | 76 | 77 | return M 78 | -------------------------------------------------------------------------------- /deployer_build_stats.csv: -------------------------------------------------------------------------------- 1 | date,sha,version,build_size,build_time,platform,mode,is_cache_using,commits_count 2 | 2023-05-20T17:48:30Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,31,arm64-ios,debug,true,11 3 | 2023-05-20T17:51:04Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,32,arm64-ios,debug,true,11 4 | 2023-05-20T17:53:37Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,31,arm64-ios,debug,true,11 5 | 2023-05-20T18:13:25Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,31,arm64-ios,debug,true,11 6 | 2023-05-20T18:16:26Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,21,arm64-ios,debug,true,11 7 | 2023-05-20T18:18:49Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,8396,35,x86_64-macos,debug,true,11 8 | 2023-05-20T18:20:37Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2520,44,arm64-ios,debug,true,11 9 | 2023-05-20T18:21:40Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,24,arm64-ios,debug,true,11 10 | 2023-05-20T18:34:41Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,38,arm64-ios,debug,true,11 11 | 2023-05-20T18:38:30Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,39,arm64-ios,debug,true,11 12 | 2023-05-20T18:40:29Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,42,arm64-ios,debug,true,11 13 | 2023-05-20T18:42:30Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2408,27,arm64-ios,debug,true,11 14 | 2023-05-20T18:45:24Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2412,41,arm64-ios,debug,true,11 15 | 2023-05-20T18:48:03Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2412,24,arm64-ios,debug,true,11 16 | 2023-05-20T18:57:40Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2412,23,arm64-ios,debug,true,11 17 | 2023-05-20T19:10:44Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2412,22,arm64-ios,debug,true,11 18 | 2023-05-20T19:14:54Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2432,33,arm64-ios,debug,true,11 19 | 2023-05-20T19:21:27Z,1a2d070e6b8b9b199294214781c08cfe52f9250a,0.0.11,2432,21,arm64-ios,debug,true,11 20 | 2023-05-21T06:00:37Z,5e948ec5be5358f511f0b63837bd76e4fd981f76,0.0.13,2452,46,arm64-ios,debug,true,13 21 | 2023-05-21T21:22:50Z,f429a957deaad7edef3bbb82e3746d614a4763df,0.0.14,2768,97,js-web,release,true,14 22 | 2023-05-21T22:42:45Z,f429a957deaad7edef3bbb82e3746d614a4763df,0.0.14,2476,43,arm64-ios,debug,true,14 23 | -------------------------------------------------------------------------------- /export_config/custom_handlers.js: -------------------------------------------------------------------------------- 1 | function tokens(data, config) { 2 | for (let key in data) { 3 | let record = data[key] 4 | let tokens = [] 5 | for (let i in config.fields) { 6 | if (!record[config.fields[i]]) { 7 | continue 8 | } 9 | let token_list = config.fields[i] 10 | 11 | for (let j = 0; j < record[token_list].length; j += 2) { 12 | tokens.push({ 13 | token_id: record[token_list][j], 14 | amount: record[token_list][j+1] 15 | }) 16 | } 17 | delete record[token_list] 18 | } 19 | if (config.id) { 20 | if (tokens.length > 0) { 21 | record[config.id] = { tokens: tokens } 22 | } 23 | } 24 | else { 25 | if (tokens.length > 0) { 26 | data[key] = { tokens: tokens } 27 | } 28 | } 29 | } 30 | 31 | return data 32 | } 33 | 34 | function quest_tasks(data, config) { 35 | for (let key in data) { 36 | let record = data[key] 37 | let tasks = [] 38 | for (let i in config.fields) { 39 | if (!record[config.fields[i]]) { 40 | continue 41 | } 42 | 43 | tasks.push({ 44 | action: record[config.fields[i]][0], 45 | object: record[config.fields[i]][1], 46 | required: record[config.fields[i]][2], 47 | param1: record[config.fields[i]][3], 48 | param2: record[config.fields[i]][4] 49 | }) 50 | 51 | delete record[config.fields[i]] 52 | } 53 | record[config.id] = tasks 54 | } 55 | 56 | return data 57 | } 58 | 59 | function ensure_array(data, config) { 60 | for (let key in data) { 61 | let record = data[key] 62 | if (record[config.field] && !Array.isArray(record[config.field])) { 63 | record[config.field] = [record[config.field]] 64 | } 65 | } 66 | 67 | return data 68 | } 69 | 70 | 71 | function ensure_string(data, config) { 72 | for (let key in data) { 73 | let record = data[key] 74 | if (Array.isArray(record[config.field])) { 75 | for (let i = 1; i < record[config.field].length; i++) { 76 | record[config.field][i] = String(record[config.field][i]) 77 | } 78 | } else { 79 | if (record[config.field]) { 80 | record[config.field] = String(record[config.field]) 81 | } 82 | } 83 | } 84 | 85 | return data 86 | } 87 | 88 | 89 | 90 | module.exports = { 91 | tokens: tokens, 92 | quest_tasks: quest_tasks, 93 | ensure_array: ensure_array, 94 | ensure_string: ensure_string 95 | } 96 | -------------------------------------------------------------------------------- /eva/modules/camera/camera_pinch.lua: -------------------------------------------------------------------------------- 1 | -- Part of Eva Camera module 2 | -- Handle the camera gestures like zoom and multitouchs 3 | -- @local 4 | 5 | 6 | local app = require("eva.app") 7 | local luax = require("eva.luax") 8 | local const = require("eva.const") 9 | local rendercam = require("rendercam.rendercam") 10 | 11 | local M = {} 12 | 13 | 14 | M.SCREEN = vmath.vector3( 15 | sys.get_config("display.width")/2, 16 | sys.get_config("display.height")/2, 17 | 0) 18 | 19 | local function handle_pinch(state, camera_state) 20 | -- Pinch delta 21 | local delta_koef = math.sqrt(camera_state.target_zoom) 22 | local delta = state.pinch_delta * delta_koef 23 | local hard = camera_state.zoom_border_hard 24 | 25 | -- Adjust zoom 26 | camera_state.target_zoom = camera_state.target_zoom - delta 27 | 28 | if camera_state.target_zoom > hard.x and camera_state.target_zoom < hard.y then 29 | -- Move to zoom position if zoom legal (in hard zone) 30 | local move_vector = vmath.vector3(state.pinch_pos.x - M.SCREEN.x, state.pinch_pos.y - M.SCREEN.y, 0) 31 | camera_state.target_pos.x = camera_state.target_pos.x + move_vector.x * delta 32 | camera_state.target_pos.y = camera_state.target_pos.y + move_vector.y * delta 33 | end 34 | end 35 | 36 | 37 | function M.handle_pinch(input_type, input_state) 38 | handle_pinch(input_state, app.camera_state) 39 | end 40 | 41 | 42 | function M.update_camera_zoom(state, dt, params) 43 | local input_state = app.input.state 44 | local soft = state.zoom_border_soft 45 | local hard = state.zoom_border_hard 46 | 47 | if state.is_animate_zoom then 48 | return 49 | end 50 | 51 | -- Soft border 52 | if not input_state.is_pinch then 53 | if state.target_zoom < soft.x then 54 | state.target_zoom = luax.math.lerp(state.target_zoom, soft.x, params.zoom_border_lerp_speed) 55 | end 56 | if state.target_zoom > soft.y then 57 | state.target_zoom = luax.math.lerp(state.target_zoom, soft.y, params.zoom_border_lerp_speed) 58 | end 59 | end 60 | 61 | -- Hard border 62 | state.target_zoom = luax.math.clamp(state.target_zoom, hard.x, hard.y) 63 | 64 | -- Lerp zoom 65 | local zoom_lerp_speed = params.zoom_lerp_speed * const.FPS * dt 66 | state.zoom = luax.math.lerp(state.zoom, state.target_zoom, zoom_lerp_speed) 67 | 68 | rendercam.set_ortho_scale(state.zoom, state.cam_id) 69 | end 70 | 71 | 72 | return M 73 | -------------------------------------------------------------------------------- /eva/modules/camera/camera_drag.lua: -------------------------------------------------------------------------------- 1 | -- Part of Eva Camera module 2 | -- Handle camera dragging 3 | -- @local 4 | 5 | 6 | local app = require("eva.app") 7 | local luax = require("eva.luax") 8 | local const = require("eva.const") 9 | local rendercam = require("rendercam.rendercam") 10 | 11 | local M = {} 12 | 13 | 14 | function M.handle_drag(input_type, input_state) 15 | local camera_state = app.camera_state 16 | 17 | local x, y = rendercam.screen_to_world_2d(input_state.dx, input_state.dy, true, nil, true) 18 | camera_state.target_pos.x = camera_state.target_pos.x - x 19 | camera_state.target_pos.y = camera_state.target_pos.y - y 20 | 21 | camera_state.inertion.x = camera_state.inertion.x - x 22 | camera_state.inertion.y = camera_state.inertion.y - y 23 | end 24 | 25 | 26 | function M.update_camera_pos(state, dt, params) 27 | local input_state = app.input.state 28 | 29 | if not state.cam_id then 30 | return 31 | end 32 | 33 | if state.is_animate then 34 | return 35 | end 36 | 37 | local pos = state.pos 38 | local target = state.target_pos 39 | local border_soft = state.border_soft 40 | local border_lerp = params.move_border_lerp_speed * const.FPS * dt 41 | 42 | -- Soft border 43 | if not input_state.is_drag and border_soft and state.is_borders_enabled then 44 | luax.math.lerp_box(target, border_soft, border_lerp, state.camera_box * state.zoom, true) 45 | end 46 | 47 | -- Inertion friction 48 | local friction_koef = input_state.is_drag and params.friction_hold or params.friction 49 | state.inertion = state.inertion * friction_koef 50 | 51 | -- Inertion apply 52 | if not input_state.is_drag then 53 | local inertion_koef = const.FPS * dt * params.inertion_koef 54 | target.x = target.x + state.inertion.x * inertion_koef 55 | target.y = target.y + state.inertion.y * inertion_koef 56 | end 57 | 58 | -- Hard border 59 | if state.border_hard and state.is_borders_enabled then 60 | luax.math.clamp_box(target, state.border_hard, state.camera_box * state.zoom, true) 61 | end 62 | 63 | if pos.x == target.x and pos.y == target.y then 64 | return 65 | end 66 | 67 | -- Lerp position 68 | pos.x = luax.math.lerp(pos.x, target.x, params.move_lerp_speed * const.FPS * dt) 69 | pos.y = luax.math.lerp(pos.y, target.y, params.move_lerp_speed * const.FPS * dt) 70 | 71 | go.set_position(state.pos, state.cam_id) 72 | end 73 | 74 | 75 | return M 76 | -------------------------------------------------------------------------------- /gui/window_info/window_info.gui_script: -------------------------------------------------------------------------------- 1 | local druid = require("druid.druid") ---@type druid 2 | 3 | local eva = require("eva.eva") ---@type eva 4 | local const = require("game.const") 5 | 6 | local WINDOW_ID = const.WINDOW.WINDOW_INFO 7 | 8 | ---@class window.info 9 | ---@field data window.info_data 10 | 11 | ---@class window.info_data 12 | ---@field text string 13 | ---@field header_text string|nil 14 | ---@field button_text string|nil 15 | ---@field is_only_agree_button boolean|nil 16 | 17 | 18 | ---@param self window.info 19 | local function close_window(self) 20 | local callbacks = self.data.callbacks 21 | eva.window.disappear(WINDOW_ID, function() 22 | if callbacks and callbacks.on_close then 23 | callbacks.on_close() 24 | end 25 | end) 26 | end 27 | 28 | 29 | ---@param self window.info 30 | function init(self) 31 | self.druid = druid.new(self) 32 | self.root = gui.get_node("root") 33 | self.data = eva.window.get_data(WINDOW_ID) 34 | 35 | if not self.data.only_button_close then 36 | self.druid:new_button("window_shadow/root", close_window) 37 | self.druid:new_back_handler(close_window) 38 | gui.set_enabled(gui.get_node("base_window/button_close/button"), false) 39 | end 40 | 41 | self.druid:new_blocker("content") 42 | self.druid:new_button("base_window/button_close/button", close_window) 43 | self.druid:new_button("button_agree/button", close_window) 44 | 45 | self.druid:new_text("base_window/text_header", self.data.header_text or eva.lang.txt("ui_header_info")) 46 | self.druid:new_text("button_agree/text", self.data.button_text or eva.lang.txt("ui_agree")) 47 | self.druid:new_text("text_info", self.data.text) 48 | 49 | self.druid:new_layout("node_upscale_limit"):set_max_gui_upscale(const.MAX_GUI_UPSCALE) 50 | 51 | eva.window.appear(WINDOW_ID) 52 | end 53 | 54 | 55 | ---@param self window.info 56 | function final(self) 57 | self.druid:final() 58 | end 59 | 60 | 61 | ---@param self window.info 62 | function update(self, dt) 63 | self.druid:update(dt) 64 | end 65 | 66 | 67 | ---@param self window.info 68 | function on_message(self, message_id, message, sender) 69 | eva.window.on_message(WINDOW_ID, message_id, message, sender) 70 | self.druid:on_message(message_id, message, sender) 71 | end 72 | 73 | 74 | ---@param self window.info 75 | function on_input(self, action_id, action) 76 | return self.druid:on_input(action_id, action) 77 | end 78 | -------------------------------------------------------------------------------- /eva/modules/db.lua: -------------------------------------------------------------------------------- 1 | --- Eva database module. 2 | -- It carry for loading all jsons from 3 | -- game configs, pointed in eva settings.json. 4 | -- You need to point name of config and his path in 5 | -- game eva settings. 6 | -- @submodule eva 7 | 8 | 9 | local app = require("eva.app") 10 | local log = require("eva.log") 11 | 12 | local utils = require("eva.modules.utils") 13 | local proto = require("eva.modules.proto") 14 | 15 | local logger = log.get_logger("eva.db") 16 | 17 | local M = {} 18 | 19 | 20 | local function verify(config_name, data) 21 | local settings = app.settings.db 22 | 23 | if settings.verify and settings.verify[config_name] then 24 | return proto.verify(settings.verify[config_name], data) 25 | end 26 | 27 | return data 28 | end 29 | 30 | --- Return config by config_name 31 | -- @function eva.db.get 32 | -- @tparam string config_name Config name from eva settings 33 | -- @treturn table Config table 34 | function M.get(config_name) 35 | assert(config_name, "You should provide the config name") 36 | 37 | if app._db_custom then 38 | local custom = app._db_custom[config_name] 39 | if custom then 40 | return custom 41 | end 42 | end 43 | 44 | local data = app._db[config_name] 45 | if not data then 46 | logger:error("The database config is not exist", { name = config_name }) 47 | end 48 | 49 | return data 50 | end 51 | 52 | 53 | --- Can override db with custom tables (useful for tests) 54 | -- @function eva.db.set_settings 55 | -- @tparam table settings Custom db settings 56 | function M.set_settings(settings) 57 | app._db_custom = settings 58 | end 59 | 60 | 61 | function M.before_eva_init() 62 | local settings = app.settings.db 63 | app._db = {} 64 | local paths = settings.paths 65 | 66 | for name, path in pairs(paths) do 67 | logger:debug("Load JSON data", { name = name, path = path }) 68 | local ok, result = pcall(utils.load_json, path) 69 | 70 | if ok then 71 | app._db[name] = result 72 | else 73 | logger:fatal("Error on loading json file", { name = name, traceback = result}) 74 | error(result) 75 | end 76 | end 77 | end 78 | 79 | 80 | function M.on_eva_init() 81 | if app._db_custom then 82 | for config_name, data in pairs(app._db_custom) do 83 | app._db_custom[config_name] = verify(config_name, data) 84 | end 85 | end 86 | 87 | for config_name, data in pairs(app._db) do 88 | app._db[config_name] = verify(config_name, data) 89 | end 90 | end 91 | 92 | 93 | return M 94 | -------------------------------------------------------------------------------- /game/profile/platform_adapters/platform_ios.lua: -------------------------------------------------------------------------------- 1 | local app = require("eva.app") 2 | local class = require("eva.libs.middleclass") 3 | local log = require("eva.log") 4 | 5 | local const = require("game.const") 6 | local profile_utils = require("game.profile.profile_utils") 7 | local PlatformBase = require("game.profile.platform_adapters.platform_base") 8 | 9 | local logger = log.get_logger("platform") 10 | 11 | ---@class platform.yandex : platform.base 12 | local PlatformiOS = class("platform.ios", PlatformBase) 13 | 14 | 15 | ---@param platform_name string 16 | ---@param data game.PlatformData 17 | function PlatformiOS:initialize(platform_name, data) 18 | PlatformBase:initialize(platform_name, data) 19 | self._is_player_inited = false 20 | self._share_url = "https://seabattle.onelink.me/DHGh/2or1jfpe?payload=%s" 21 | end 22 | 23 | 24 | ---@param callback func Promise resolver 25 | function PlatformiOS:login(callback) 26 | -- callback() 27 | if siwa.is_supported() then 28 | print("Sign in with Apple is supported") 29 | else 30 | print("Sign in with Apple is not supported") 31 | end 32 | 33 | siwa.authenticate(function(_, data) 34 | print(data.identity_token) 35 | print(data.user_id) 36 | print(data.first_name, data.family_name) 37 | print(data.email) 38 | if data.user_status == siwa.STATUS_LIKELY_REAL then 39 | print("Likely a real person") 40 | end 41 | pprint(data) 42 | callback() 43 | end) 44 | end 45 | 46 | 47 | ---@param user_id string 48 | function PlatformiOS:on_login(user_id) 49 | self._is_player_inited = true 50 | self._data.apple_data.user_id = user_id 51 | self._data.apple_data.is_connected = true 52 | -- self._data.apple_data.name = gpgs.get_display_name() 53 | end 54 | 55 | 56 | ---@param match_key string 57 | ---@return string|nil 58 | function PlatformiOS:get_share_url(match_key) 59 | local payload = "duel_general_" .. (match_key or "") 60 | return string.format(self._share_url, payload) 61 | end 62 | 63 | 64 | ---@param text string 65 | ---@param on_success function|nil 66 | ---@param on_error function|nil 67 | function PlatformiOS:write_to_clipboard(text, on_success, on_error) 68 | if clipboard then 69 | clipboard.copy(text) 70 | if on_success then 71 | on_success() 72 | end 73 | return 74 | end 75 | if on_error then 76 | on_error() 77 | end 78 | end 79 | 80 | 81 | function PlatformiOS:send_save_to_cloud() 82 | end 83 | 84 | 85 | function PlatformiOS:is_share_available() 86 | return true 87 | end 88 | 89 | 90 | return PlatformiOS 91 | -------------------------------------------------------------------------------- /game/analytics/analytics_gameanalytics.lua: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local luax = require("eva.luax") 3 | 4 | local Analytics_GameAnalytics = {} 5 | 6 | 7 | ---@param user_id string 8 | function Analytics_GameAnalytics.set_user_id(user_id) 9 | gameanalytics.configureUserId(user_id) 10 | end 11 | 12 | 13 | function Analytics_GameAnalytics._on_ads_success(params) 14 | local is_rewarded = params.is_rewarded 15 | local ad_type = is_rewarded and "RewardedVideo" or "Interstitial" 16 | gameanalytics.addAdEvent({ 17 | adAction = "Show", 18 | adType = ad_type, 19 | adSdkName = eva.ads.get_adapter_name(), 20 | adPlacement = params.id 21 | }) 22 | 23 | -- The reason what AdEvent is not working in HTML5... 24 | gameanalytics.addDesignEvent({ 25 | eventId = string.format("AdsShow:%s:%s", ad_type, params.id) 26 | }) 27 | end 28 | 29 | 30 | function Analytics_GameAnalytics._on_ads_error(params) 31 | local is_rewarded = params.is_rewarded 32 | local ad_type = is_rewarded and "RewardedVideo" or "Interstitial" 33 | gameanalytics.addAdEvent({ 34 | adAction = "FailedShow", 35 | adType = is_rewarded and "RewardedVideo" or "Interstitial", 36 | adSdkName = eva.ads.get_adapter_name(), 37 | adPlacement = params.id 38 | }) 39 | 40 | -- The reason what AdEvent is not working in HTML5... 41 | gameanalytics.addDesignEvent({ 42 | eventId = string.format("AdsError:%s:%s", ad_type, params.id) 43 | }) 44 | end 45 | 46 | 47 | function Analytics_GameAnalytics._on_ads_reward(params) 48 | gameanalytics.addAdEvent({ 49 | adAction = "RewardReceived", 50 | adType = "RewardedVideo", 51 | adSdkName = eva.ads.get_adapter_name(), 52 | adPlacement = params.id 53 | }) 54 | 55 | -- The reason what AdEvent is not working in HTML5... 56 | gameanalytics.addDesignEvent({ 57 | eventId = string.format("AdsReward:%s:%s", "RewardedVideo", params.id) 58 | }) 59 | end 60 | 61 | 62 | -- local AVAILABLE_ITEMS = { const.ITEM.LEVEL, const.ITEM.GOLD, const.ITEM.RATING } 63 | local AVAILABLE_ITEMS = { } 64 | function Analytics_GameAnalytics._on_token_change(params) 65 | local delta = params.delta 66 | local token_id = params.token_id 67 | local reason = params.reason 68 | local details = params.details 69 | 70 | if not luax.table.contains(AVAILABLE_ITEMS, token_id) then 71 | return 72 | end 73 | 74 | gameanalytics.addResourceEvent({ 75 | flowType = delta > 0 and "Source" or "Sink", 76 | currency = token_id, 77 | amount = math.abs(delta), 78 | itemType = reason or "Unknown", 79 | itemId = details or "Unknown", 80 | }) 81 | end 82 | 83 | 84 | return Analytics_GameAnalytics 85 | -------------------------------------------------------------------------------- /game/profile/platform_adapters/platform_web.lua: -------------------------------------------------------------------------------- 1 | local class = require("eva.libs.middleclass") 2 | local log = require("eva.log") 3 | local evaconst = require("eva.const") 4 | 5 | local profile_utils = require("game.profile.profile_utils") 6 | local PlatformBase = require("game.profile.platform_adapters.platform_base") 7 | 8 | --- This dependencies will be overriden in init 9 | local yagames = nil 10 | 11 | local logger = log.get_logger("platform") 12 | 13 | ---@class platform.yandex : platform.base 14 | local PlatformWeb = class("platform.web", PlatformBase) 15 | 16 | 17 | ---@param platform_name string 18 | ---@param data game.PlatformData 19 | function PlatformWeb:initialize(platform_name, data) 20 | PlatformBase:initialize(platform_name, data) 21 | 22 | yagames = evaconst.require("yagames.yagames") 23 | 24 | local origin = html5.run('window.location.origin;') or "" 25 | local pathname = html5.run('window.location.pathname;') or "" 26 | local search = html5.run('window.location.search;') or "" 27 | local payload = "payload=%s" 28 | 29 | if not string.find(search, "?") then 30 | payload = "?" .. payload 31 | end 32 | 33 | self._share_url = origin .. pathname .. search .. payload 34 | logger:info("Generate share url template", { template = self._share_url }) 35 | end 36 | 37 | 38 | ---@param callback func Promise resolver 39 | function PlatformWeb:login(callback) 40 | callback() 41 | end 42 | 43 | 44 | ---@param user_id string 45 | function PlatformWeb:on_login(user_id) 46 | end 47 | 48 | 49 | function PlatformWeb:get_payload() 50 | if not html5 then 51 | return 52 | end 53 | 54 | local payload = html5.run("new URLSearchParams(window.location.search).get('payload');") 55 | return payload 56 | end 57 | 58 | 59 | ---@param match_key string 60 | ---@return string|nil 61 | function PlatformWeb:get_share_url(match_key) 62 | local payload = "duel_general_" .. (match_key or "") 63 | return string.format(self._share_url, payload) 64 | end 65 | 66 | 67 | ---@param text string 68 | ---@param on_success function|nil 69 | ---@param on_error function|nil 70 | function PlatformWeb:write_to_clipboard(text, on_success, on_error) 71 | if not html5 then 72 | return 73 | end 74 | 75 | html5.run(string.format('navigator.clipboard.writeText("%s");', text)) 76 | if on_success then 77 | on_success() 78 | end 79 | end 80 | 81 | 82 | function PlatformWeb:is_share_available() 83 | return true 84 | end 85 | 86 | 87 | function PlatformWeb:send_save_to_cloud() 88 | local save_data = profile_utils.get_profile_for_cloud() 89 | yagames.player_set_data(save_data, false, function(_) 90 | logger:info("Sent save to Yandex") 91 | end) 92 | end 93 | 94 | 95 | return PlatformWeb 96 | -------------------------------------------------------------------------------- /gui/components/button_close.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | textures { 3 | name: "window_general" 4 | texture: "/assets/atlases/window_general.atlas" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 0.0 15 | y: 8.0 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 96.0 33 | y: 96.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | w: 1.0 42 | } 43 | type: TYPE_BOX 44 | blend_mode: BLEND_MODE_ALPHA 45 | texture: "window_general/button_close" 46 | id: "button" 47 | xanchor: XANCHOR_NONE 48 | yanchor: YANCHOR_NONE 49 | pivot: PIVOT_CENTER 50 | adjust_mode: ADJUST_MODE_FIT 51 | layer: "general" 52 | inherit_alpha: true 53 | slice9 { 54 | x: 0.0 55 | y: 0.0 56 | z: 0.0 57 | w: 0.0 58 | } 59 | clipping_mode: CLIPPING_MODE_NONE 60 | clipping_visible: true 61 | clipping_inverted: false 62 | alpha: 1.0 63 | template_node_child: false 64 | size_mode: SIZE_MODE_MANUAL 65 | custom_type: 0 66 | enabled: true 67 | visible: false 68 | } 69 | nodes { 70 | position { 71 | x: 0.0 72 | y: 0.0 73 | z: 0.0 74 | w: 1.0 75 | } 76 | rotation { 77 | x: 0.0 78 | y: 0.0 79 | z: 0.0 80 | w: 1.0 81 | } 82 | scale { 83 | x: 1.0 84 | y: 1.0 85 | z: 1.0 86 | w: 1.0 87 | } 88 | size { 89 | x: 200.0 90 | y: 100.0 91 | z: 0.0 92 | w: 1.0 93 | } 94 | color { 95 | x: 1.0 96 | y: 1.0 97 | z: 1.0 98 | w: 1.0 99 | } 100 | type: TYPE_BOX 101 | blend_mode: BLEND_MODE_ALPHA 102 | texture: "window_general/button_close" 103 | id: "icon" 104 | xanchor: XANCHOR_NONE 105 | yanchor: YANCHOR_NONE 106 | pivot: PIVOT_CENTER 107 | adjust_mode: ADJUST_MODE_FIT 108 | parent: "button" 109 | layer: "general" 110 | inherit_alpha: true 111 | slice9 { 112 | x: 0.0 113 | y: 0.0 114 | z: 0.0 115 | w: 0.0 116 | } 117 | clipping_mode: CLIPPING_MODE_NONE 118 | clipping_visible: true 119 | clipping_inverted: false 120 | alpha: 1.0 121 | template_node_child: false 122 | size_mode: SIZE_MODE_AUTO 123 | custom_type: 0 124 | enabled: true 125 | visible: true 126 | } 127 | layers { 128 | name: "general" 129 | } 130 | material: "/builtins/materials/gui.material" 131 | adjust_reference: ADJUST_REFERENCE_PARENT 132 | max_nodes: 512 133 | -------------------------------------------------------------------------------- /eva/modules/pathfinder.lua: -------------------------------------------------------------------------------- 1 | --- Eva pathfinder module 2 | -- It carry on path finding on some map structure (grid or hex) 3 | -- @submodule eva 4 | 5 | 6 | local astar = require("eva.libs.astar.astar") 7 | 8 | local log = require("eva.log") 9 | local app = require("eva.app") 10 | 11 | local logger = log.get_logger("eva.pathfinder") 12 | 13 | local M = {} 14 | 15 | 16 | --- Init astar for map, init get_tile callback 17 | -- get_node_fn - function to get tile: function(i, j) 18 | -- should return cost of node. Nil if cell is unpassable 19 | -- @function eva.pathfinder.init_astar 20 | -- @tparam map_data map_data Map data from eva.tiled.load_map 21 | -- @tparam function get_node_fn Get node cost function from map 22 | -- @tparam table options Options for map handlers: 23 | -- - diagonal boolean, to grid and isogrid pathfinding 24 | -- @treturn map_handler Handler for astar work 25 | function M.init_astar(map_data, get_node_fn, options) 26 | local handler = map_data.astar_handler 27 | 28 | local map_handler = handler.new_handler(get_node_fn, options) 29 | app.clear("pathfinder_default_handler") 30 | app.pathfinder_default_handler = map_handler 31 | 32 | return map_handler 33 | end 34 | 35 | 36 | --- Return path between two points for map. 37 | -- Call init_astar, to make map_handler param optional 38 | -- @function eva.pathfinder.path 39 | -- @param from_x Cell X from map 40 | -- @param from_y Cell Y from map 41 | -- @param to_x Cell X from map 42 | -- @param to_y Cell Y from map 43 | -- @tparam[opt] map_handler map_handler Map handler to handle map for astar 44 | -- @treturn table|nil Table of points. See eva.libs.astar.path. Nil if path is not exist 45 | function M.path(from_x, from_y, to_x, to_y, map_handler) 46 | map_handler = map_handler or app.pathfinder_default_handler 47 | 48 | if not map_handler then 49 | logger:error("No map handler for pathfinder") 50 | return false 51 | end 52 | 53 | return astar.find_path(map_handler, from_x, from_y, to_x, to_y) 54 | end 55 | 56 | 57 | function M.floodfill(from_x, from_y, map_handler) 58 | map_handler = map_handler or app.pathfinder_default_handler 59 | 60 | if not map_handler then 61 | logger:error("No map handler for floodfill") 62 | return false 63 | end 64 | 65 | return astar.floodfill(map_handler, from_x, from_y) 66 | end 67 | 68 | 69 | function M.for_path(path, callback) 70 | if not path then 71 | return nil 72 | end 73 | 74 | for i = 1, #path.nodes do 75 | local x = path.nodes[i].x 76 | local y = path.nodes[i].y 77 | callback(x, y, i) 78 | end 79 | end 80 | 81 | 82 | function M.get_cost(i, j, map_handler) 83 | map_handler = map_handler or app.pathfinder_default_handler 84 | return map_handler.get_cost(i, j) 85 | end 86 | 87 | 88 | return M 89 | -------------------------------------------------------------------------------- /eva/libs/sequence.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | local function resume(co) 5 | local _, err = coroutine.resume(co) 6 | if err then print(err) end 7 | end 8 | 9 | function M.run_once(fn, ...) 10 | coroutine.wrap(fn)(...) 11 | end 12 | 13 | function M.run_loop(fn, ...) 14 | coroutine.wrap(function(...) 15 | while true do 16 | fn(...) 17 | end 18 | end)(...) 19 | end 20 | 21 | function M.delay(seconds) 22 | local co = coroutine.running() 23 | assert(co, "You must call this from inside a sequence") 24 | timer.delay(seconds, false, function() 25 | resume(co) 26 | end) 27 | coroutine.yield() 28 | end 29 | 30 | function M.gui_animate(node, property, to, easing, duration, delay, playback) 31 | local co = coroutine.running() 32 | assert(co, "You must call this from inside a sequence") 33 | gui.animate(node, property, to, easing, duration or 0, delay or 0, function() 34 | resume(co) 35 | end, playback) 36 | coroutine.yield() 37 | end 38 | 39 | function M.go_animate(url, property, playback, to, easing, duration, delay) 40 | local co = coroutine.running() 41 | assert(co, "You must call this from inside a sequence") 42 | go.animate(url, property, playback, to, easing, duration or 0, delay or 0, function() 43 | resume(co) 44 | end, playback) 45 | coroutine.yield() 46 | end 47 | 48 | function M.spine_play_anim(url, anim_id, playback, play_properties) 49 | local co = coroutine.running() 50 | assert(co, "You must call this from inside a sequence") 51 | spine.play_anim(url, anim_id, playback, play_properties, function(self, message_id, message, sender) 52 | resume(co) 53 | end) 54 | coroutine.yield() 55 | end 56 | 57 | function M.gui_play_flipbook(node, id) 58 | local co = coroutine.running() 59 | assert(co, "You must call this from inside a sequence") 60 | gui.play_flipbook(node, id, function() 61 | resume(co) 62 | end) 63 | coroutine.yield() 64 | end 65 | 66 | function M.sprite_play_flipbook(url, id) 67 | local co = coroutine.running() 68 | assert(co, "You must call this from inside a sequence") 69 | sprite.play_flipbook(url, id, function() 70 | resume(co) 71 | end) 72 | coroutine.yield() 73 | end 74 | 75 | function M.http_request(url, method, headers, post_data, options) 76 | local co = coroutine.running() 77 | assert(co, "You must call this from inside a sequence") 78 | http.request(url, method, function(self, id, response) 79 | resume(co, response) 80 | end,headers, post_data, options) 81 | return coroutine.yield() 82 | end 83 | 84 | function M.http_get(url, headers, options) 85 | return M.http_request(url, "GET", headers, nil, options) 86 | end 87 | 88 | function M.http_post(url, headers, post_data, options) 89 | return M.http_request(url, "POST", headers, post_data, options) 90 | end 91 | 92 | 93 | return M 94 | -------------------------------------------------------------------------------- /gui/scene_menu/scene_menu.gui_script: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local app = require("eva.app") 3 | local luax = require("eva.luax") 4 | local const = require("game.const") 5 | local druid = require("druid.druid") 6 | local druid_const = require("druid.const") 7 | 8 | 9 | ---@class scene.menu 10 | 11 | 12 | ---@param self scene.menu 13 | local function on_button_play(self) 14 | eva.window.show_scene(const.WINDOW.SCENE_GAME) 15 | end 16 | 17 | 18 | ---@param self scene.game 19 | local function on_button_menu(self) 20 | eva.window.show(const.WINDOW.WINDOW_SETTINGS, { 21 | hide_exit_button = true, 22 | }) 23 | end 24 | 25 | 26 | ---@param self scene.menu 27 | local function on_button_admin(self) 28 | eva.window.show(const.WINDOW.WINDOW_ADMIN) 29 | end 30 | 31 | 32 | ---@param self scene.menu 33 | local function on_connection_problem(self) 34 | eva.window.show(const.WINDOW.WINDOW_INFO, { 35 | text = eva.lang.txt("ui_try_to_connect_info") 36 | }) 37 | end 38 | 39 | 40 | ---@param self scene.menu 41 | local function on_connection_status_change(self) 42 | gui.set_enabled(self._node_wifi, not eva.server.is_connected()) 43 | end 44 | 45 | 46 | ---@param self scene.menu 47 | function init(self) 48 | self.druid = druid.new(self) 49 | 50 | self.button_menu = self.druid:new_button("button_menu/button", on_button_menu) 51 | self.button_play = self.druid:new_button("button_play/button", on_button_play) 52 | self.button_admin = self.druid:new_button("button_admin/button", on_button_admin) 53 | self.druid:new_layout(gui.get_node("background"), druid_const.LAYOUT_MODE.ZOOM_MAX) 54 | :fit_into_window() 55 | 56 | self._node_wifi = gui.get_node("icon_wifi") 57 | gui.animate(self._node_wifi, luax.gui.PROP_ALPHA, 0, gui.EASING_OUTSINE, 1, 0, nil, gui.PLAYBACK_LOOP_PINGPONG) 58 | self.druid:new_button(self._node_wifi, on_connection_problem) 59 | 60 | on_connection_status_change(self) 61 | app.server_events.connect_status:subscribe(on_connection_status_change, self) 62 | app.platform.on_new_payload:subscribe(on_connection_status_change, self) 63 | 64 | local insets = safearea.get_insets() 65 | luax.gui.add_y(gui.get_node("NE_Anchor"), -insets.top / 2) 66 | luax.gui.add_y(gui.get_node("NW_Anchor"), -insets.top / 2) 67 | end 68 | 69 | 70 | ---@param self scene.menu 71 | function final(self) 72 | self.druid:final() 73 | end 74 | 75 | 76 | ---@param self scene.menu 77 | function update(self, dt) 78 | self.druid:update(dt) 79 | end 80 | 81 | 82 | ---@param self scene.menu 83 | function on_message(self, message_id, message, sender) 84 | self.druid:on_message(message_id, message, sender) 85 | end 86 | 87 | 88 | ---@param self scene.menu 89 | function on_input(self, action_id, action) 90 | return self.druid:on_input(action_id, action) 91 | end 92 | -------------------------------------------------------------------------------- /gui/components/button_text.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | fonts { 3 | name: "game" 4 | font: "/assets/fonts/game.font" 5 | } 6 | textures { 7 | name: "window_general" 8 | texture: "/assets/atlases/window_general.atlas" 9 | } 10 | background_color { 11 | x: 0.0 12 | y: 0.0 13 | z: 0.0 14 | w: 0.0 15 | } 16 | nodes { 17 | position { 18 | x: 0.0 19 | y: 0.0 20 | z: 0.0 21 | w: 1.0 22 | } 23 | rotation { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | w: 1.0 28 | } 29 | scale { 30 | x: 1.0 31 | y: 1.0 32 | z: 1.0 33 | w: 1.0 34 | } 35 | size { 36 | x: 265.0 37 | y: 110.0 38 | z: 0.0 39 | w: 1.0 40 | } 41 | color { 42 | x: 1.0 43 | y: 1.0 44 | z: 1.0 45 | w: 1.0 46 | } 47 | type: TYPE_BOX 48 | blend_mode: BLEND_MODE_ALPHA 49 | texture: "window_general/button_green" 50 | id: "button" 51 | xanchor: XANCHOR_NONE 52 | yanchor: YANCHOR_NONE 53 | pivot: PIVOT_CENTER 54 | adjust_mode: ADJUST_MODE_FIT 55 | layer: "" 56 | inherit_alpha: true 57 | slice9 { 58 | x: 24.0 59 | y: 24.0 60 | z: 24.0 61 | w: 24.0 62 | } 63 | clipping_mode: CLIPPING_MODE_NONE 64 | clipping_visible: true 65 | clipping_inverted: false 66 | alpha: 1.0 67 | template_node_child: false 68 | size_mode: SIZE_MODE_MANUAL 69 | custom_type: 0 70 | enabled: true 71 | visible: true 72 | } 73 | nodes { 74 | position { 75 | x: 0.0 76 | y: 0.0 77 | z: 0.0 78 | w: 1.0 79 | } 80 | rotation { 81 | x: 0.0 82 | y: 0.0 83 | z: 0.0 84 | w: 1.0 85 | } 86 | scale { 87 | x: 1.0 88 | y: 1.0 89 | z: 1.0 90 | w: 1.0 91 | } 92 | size { 93 | x: 235.0 94 | y: 90.0 95 | z: 0.0 96 | w: 1.0 97 | } 98 | color { 99 | x: 0.9607843 100 | y: 0.92941177 101 | z: 0.8039216 102 | w: 1.0 103 | } 104 | type: TYPE_TEXT 105 | blend_mode: BLEND_MODE_ALPHA 106 | text: "DEFENCE" 107 | font: "game" 108 | id: "text" 109 | xanchor: XANCHOR_NONE 110 | yanchor: YANCHOR_NONE 111 | pivot: PIVOT_CENTER 112 | outline { 113 | x: 0.2 114 | y: 0.4 115 | z: 0.4 116 | w: 1.0 117 | } 118 | shadow { 119 | x: 1.0 120 | y: 1.0 121 | z: 1.0 122 | w: 1.0 123 | } 124 | adjust_mode: ADJUST_MODE_FIT 125 | line_break: false 126 | parent: "button" 127 | layer: "text" 128 | inherit_alpha: true 129 | alpha: 1.0 130 | outline_alpha: 0.0 131 | shadow_alpha: 0.0 132 | template_node_child: false 133 | text_leading: 1.0 134 | text_tracking: 0.01 135 | custom_type: 0 136 | enabled: true 137 | visible: true 138 | } 139 | layers { 140 | name: "general" 141 | } 142 | layers { 143 | name: "text" 144 | } 145 | material: "/builtins/materials/gui.material" 146 | adjust_reference: ADJUST_REFERENCE_PARENT 147 | max_nodes: 512 148 | -------------------------------------------------------------------------------- /settings_deployer: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # lhttps://github.com/Insality/defold-deployer 3 | 4 | # You can point bob version for project in format "filename:sha" 5 | bob_sha="145:3dbbf1dbebd3a8146f6a917d101882a61f56afdc" 6 | 7 | # Select Defold channel. Values: stable, beta, alpha 8 | bob_channel="stable" 9 | 10 | # If true, it will check and download latest bob version. It will ignore bob_sha param 11 | use_latest_bob=false 12 | 13 | # Select Defold build server 14 | build_server="https://build.defold.com" 15 | 16 | # Pre-build hook bash script path. The path relative from game project folder 17 | pre_build_script=false 18 | 19 | # Post-build hook bash script path. The path relative from game project folder 20 | post_build_script=false 21 | 22 | # Set patch (last value after dot) game version value as total git commits count (1.2.0 -> 1.2.{commits_count}) 23 | # You allow to get SHA commit from version via: git rev-list --all --reverse | sed -n {N}p 24 | enable_incremental_version=true 25 | 26 | # Use git commits count as android.version_code on build 27 | enable_incremental_android_version_code=true 28 | 29 | # Local resource cache folder for deployer script. This folder will be added to gitignore if exists 30 | resource_cache_local=".cache_deployer" 31 | 32 | # Exclude folders from build 33 | exclude_folders="server" 34 | 35 | # Use settings file for Android platform 36 | settings_android=./settings/android.ini 37 | 38 | # Use settings file for iOS platform 39 | settings_ios=./settings/ios.ini 40 | 41 | # Path to android keystore for debug 42 | android_keystore_dev="./provisions/seabattle.keystore" 43 | 44 | # Path to android keystore for release 45 | android_keystore_dist="./provisions/seabattle.keystore" 46 | 47 | # Path to android keystore password for debug. This file should contains keystore password 48 | android_keystore_password_dev="./provisions/password.txt" 49 | 50 | # Path to android keystore password for release. This file should contains keystore password 51 | android_keystore_password_dist="./provisions/password.txt" 52 | 53 | # Name of alias from provided keystore to use for android development build 54 | android_keystore_alias_dev="seabattle" 55 | 56 | # Name of alias from provided keystore to use for android release build 57 | android_keystore_alias_dist="seabattle" 58 | 59 | # ID of your ios development identity 60 | ios_identity_dev="6JS55X769D" 61 | 62 | # ID of your iod distribution identity 63 | ios_identity_dist="6JS55X769D" 64 | 65 | # Path to ios development mobileprovision 66 | ios_prov_dev="./provisions/ios_dev.mobileprovision" 67 | 68 | # Path to ios distribution mobileprovision 69 | ios_prov_dist="./provisions/ios_dev.mobileprovision" 70 | 71 | # If true, add `-l yes` build param for publish live content 72 | is_live_content=false 73 | 74 | # Set to true, if you do not need to strip executables 75 | no_strip_executable=false 76 | 77 | # Is need to build html report 78 | is_build_html_report=true 79 | 80 | -------------------------------------------------------------------------------- /eva/modules/rate.lua: -------------------------------------------------------------------------------- 1 | --- Eva rate module 2 | -- Can promt to user rate the game 3 | -- Can store rate settings and handle basic 4 | -- logic to when need to show rate or enough 5 | -- @submodule eva 6 | 7 | 8 | local app = require("eva.app") 9 | local const = require("eva.const") 10 | 11 | local game = require("eva.modules.game") 12 | local saver = require("eva.modules.saver") 13 | local proto = require("eva.modules.proto") 14 | local events = require("eva.modules.events") 15 | 16 | local M = {} 17 | 18 | M.ADAPTERS = { 19 | ["stub"] = "eva.modules.rate.rate_stub", 20 | ["mobile"] = "eva.modules.rate.rate_mobile", 21 | ["yandex"] = "eva.modules.rate.rate_yandex" 22 | } 23 | 24 | 25 | local function get_adapter_instance() 26 | return app._rate_data.adapter_instance 27 | end 28 | 29 | 30 | --- Set never promt rate again 31 | -- @function eva.rate.set_never_show 32 | function M.set_never_show(state) 33 | app[const.EVA.RATE].is_never_show = state 34 | end 35 | 36 | 37 | --- Set rate as accepted. It will no show more 38 | -- @function eva.rate.set_accepted 39 | function M.set_accepted(state) 40 | app[const.EVA.RATE].is_accepted = state 41 | end 42 | 43 | 44 | --- Try to promt rate game to the player 45 | -- @function eva.rate.promt_rate 46 | function M.promt_rate(on_can_promt) 47 | M.is_can_promt_now(function(is_can_promt) 48 | if not is_can_promt then 49 | return 50 | end 51 | 52 | app[const.EVA.RATE].promt_count = app[const.EVA.RATE].promt_count + 1 53 | 54 | if on_can_promt then 55 | on_can_promt() 56 | end 57 | end) 58 | end 59 | 60 | 61 | --- Return can promt rate now or not 62 | -- @function eva.rate.is_can_promt_now 63 | -- @tparam function callback The callback(boolean) on check if we can promt now. 64 | function M.is_can_promt_now(callback) 65 | local settings = app.settings.rate 66 | if app[const.EVA.RATE].is_never_show or app[const.EVA.RATE].is_accepted then 67 | return callback(false) 68 | end 69 | 70 | if app[const.EVA.RATE].promt_count > settings.max_promt_count then 71 | return callback(false) 72 | end 73 | 74 | return get_adapter_instance().is_can_show(callback) 75 | end 76 | 77 | 78 | --- Open store or native rating if available 79 | -- @function eva.rate.open_rate 80 | function M.open_rate() 81 | events.event(const.EVENT.RATE_OPEN) 82 | if get_adapter_instance().is_supported() then 83 | get_adapter_instance().request_review() 84 | else 85 | game.open_store_page() 86 | end 87 | end 88 | 89 | 90 | function M.on_eva_init() 91 | app[const.EVA.RATE] = proto.get(const.EVA.RATE) 92 | saver.add_save_part(const.EVA.RATE, app[const.EVA.RATE]) 93 | 94 | local adapter = sys.get_config("eva.rate_provider", app.settings.rate.adapter) or "mobile" 95 | app._rate_data = { 96 | adapter = adapter, 97 | adapter_instance = const.require(M.ADAPTERS[adapter]) 98 | } 99 | end 100 | 101 | 102 | return M 103 | -------------------------------------------------------------------------------- /server/modules/user_codes.lua: -------------------------------------------------------------------------------- 1 | 2 | local const = require("game.const") 3 | local nakama = require("nakama") ---@type nakama 4 | 5 | 6 | local SYMBOL_FILL = "A" 7 | local SYMBOL_MAP = { 8 | "B", "2", "T", "5", "C", "E", 9 | "N", "F", "K", "S", "P", "R", 10 | "G", "3", "Y", "8", "M", "Z", 11 | "9", "D", "X", "6", "4", "L", 12 | "V", "H", "J", "W", "U" 13 | } 14 | local CODE_MIN_LENGTH = 4 15 | 16 | local function generate_code(users_count) 17 | local length = #SYMBOL_MAP 18 | local code = "" 19 | 20 | local count_left = users_count 21 | while count_left > length do 22 | local new_count_left = math.floor(count_left / length) 23 | local char_index = (count_left - (new_count_left * length)) + 1 24 | code = SYMBOL_MAP[char_index] .. code 25 | count_left = new_count_left 26 | end 27 | if count_left > 0 then 28 | code = SYMBOL_MAP[count_left] .. code 29 | end 30 | while #code < CODE_MIN_LENGTH do 31 | code = SYMBOL_FILL .. code 32 | end 33 | 34 | return code 35 | end 36 | 37 | 38 | -- Register Request Before 39 | ---@param context Context 40 | ---@param payload table 41 | local function set_user_code(context, payload) 42 | local user_id = context.user_id 43 | 44 | nakama.logger_info("Context: " .. nakama.json_encode(context) .. " , " .. nakama.json_encode(payload)) 45 | ---@type CollectionObject[] 46 | local collection_objects_read = { 47 | { collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, key = const.SERVER_STORAGE.USER_CODE_TOTAL_USERS_KEY}, 48 | { collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, key = const.SERVER_STORAGE.USER_CODE_KEY, user_id = user_id } 49 | } 50 | 51 | local total_users = 1 52 | local db_users_result = nakama.storage_read(collection_objects_read) 53 | nakama.logger_info("Got result: " .. nakama.json_encode(db_users_result)) 54 | 55 | if db_users_result[1] then 56 | total_users = db_users_result[1].value.value or total_users 57 | total_users = tonumber(total_users) 58 | end 59 | nakama.logger_info("Get total users: " .. total_users) 60 | 61 | if db_users_result[2] and db_users_result[2].value.value then 62 | nakama.logger_info("User code already exists: " .. db_users_result[2].value.value) 63 | -- don't do anything 64 | return payload 65 | end 66 | 67 | local user_code = generate_code(total_users) 68 | ---@type CollectionObject[] 69 | local collection_objects_write = { 70 | { collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, key = const.SERVER_STORAGE.USER_CODE_TOTAL_USERS_KEY, value = { value = total_users + 1 } }, 71 | { collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, key = const.SERVER_STORAGE.USER_CODE_KEY, value = { value = user_code }, user_id = user_id, permission_read = 2 }, 72 | { collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, key = user_code, value = { value = user_id }, permission_read = 2 } 73 | } 74 | nakama.storage_write(collection_objects_write) 75 | 76 | nakama.logger_info("Write user code: " .. user_code) 77 | 78 | return payload 79 | end 80 | 81 | nakama.register_req_after(set_user_code, "AuthenticateDevice") 82 | -------------------------------------------------------------------------------- /game/server/Server.lua: -------------------------------------------------------------------------------- 1 | local app = require("eva.app") 2 | local eva = require("eva.eva") 3 | local log = require("eva.log") 4 | local Event = require("eva.event") 5 | local nakama = require("nakama.nakama") 6 | local class = require("eva.libs.middleclass") 7 | 8 | local ServerRequester = require("game.server.ServerRequester") 9 | 10 | local logger = log.get_logger("server") 11 | 12 | 13 | ---@class Server 14 | ---@field requester ServerRequester 15 | ---@field _event_table table 16 | local Server = class("server") 17 | 18 | 19 | function Server:initialize() 20 | self._is_connected = false 21 | self.on_message_send = Event() 22 | self.on_message_recieve = Event() 23 | self._socket = nil 24 | self.requester = ServerRequester(self) 25 | 26 | self._event_table = { 27 | } 28 | 29 | app.server_events.event_matchdata:subscribe(self._on_match_data, self) 30 | app.server_events.event_matchmakermatched:subscribe(self._on_matchmaker_matched, self) 31 | app.server_events.event_notification:subscribe(self._on_notification, self) 32 | end 33 | 34 | 35 | function Server:finalize() 36 | app.server_events.event_matchdata:unsubscribe(self._on_match_data, self) 37 | app.server_events.event_matchmakermatched:unsubscribe(self._on_matchmaker_matched, self) 38 | app.server_events.event_notification:unsubscribe(self._on_notification, self) 39 | end 40 | 41 | 42 | function Server:disconnect() 43 | 44 | end 45 | 46 | 47 | ---@param message_id client_server_message_id 48 | ---@param message table 49 | ---@param callback func 50 | function Server:send(message_id, message, callback) 51 | message = message or {} 52 | self.on_message_send:trigger(message_id, message) 53 | 54 | local socket = eva.server.get_socket() 55 | nakama.socket_send(socket, message, function(response) 56 | if callback then 57 | callback(response) 58 | end 59 | end) 60 | end 61 | 62 | 63 | ---@param message_id string 64 | ---@param message table 65 | function Server:on_message(message_id, message, data) 66 | self.on_message_recieve:trigger(message_id, message) 67 | if self._event_table[message_id] then 68 | self._event_table[message_id]:trigger(message) 69 | end 70 | end 71 | 72 | 73 | ---@param notifications Notification[] 74 | function Server:_on_notification(notifications) 75 | for index = 1, #notifications do 76 | local notification = notifications[index] 77 | local event = self._event_table[notification.code] 78 | if event then 79 | if type(notification.content) == "string" then 80 | notification.content = json.decode(notification.content) 81 | end 82 | self:on_message(notification.code, notification) 83 | end 84 | end 85 | end 86 | 87 | 88 | ---@return boolean 89 | function Server:is_connected() 90 | return self._is_connected 91 | end 92 | 93 | 94 | function Server:_on_match_data(message) 95 | local match_data = message.match_data 96 | local operation_code = tonumber(match_data.op_code) 97 | end 98 | 99 | 100 | function Server:_on_matchmaker_matched(message) 101 | end 102 | 103 | 104 | return Server 105 | -------------------------------------------------------------------------------- /game/profile/Platform.lua: -------------------------------------------------------------------------------- 1 | local eva = require("eva.eva") ---@type eva 2 | local app = require("eva.app") 3 | local class = require("eva.libs.middleclass") 4 | local log = require("eva.log") 5 | 6 | local platform_base = require("game.profile.platform_adapters.platform_base") 7 | local platform_web = require("game.profile.platform_adapters.platform_web") 8 | local platform_yandex = require("game.profile.platform_adapters.platform_yandex") 9 | local platform_ios = require("game.profile.platform_adapters.platform_ios") 10 | local platform_android = require("game.profile.platform_adapters.platform_android") 11 | 12 | local logger = log.get_logger("platform") 13 | 14 | local PLATFORM_MAP = { 15 | ["stub"] = platform_base, 16 | ["web"] = platform_web, 17 | ["yandex"] = platform_yandex, 18 | ["android"] = platform_android, 19 | ["ios"] = platform_ios, 20 | } 21 | 22 | ---@class Platform 23 | ---@field data game.PlatformData 24 | ---@field on_new_payload eva.events 25 | local Platform = class("game.Platform") 26 | 27 | 28 | function Platform:initialize() 29 | ---@type game.PlatformData 30 | self.data = eva.proto.get("game.PlatformData") 31 | eva.saver.add_save_part("game.PlatformData", self.data) 32 | self._platform = self:_get_platform_class(sys.get_config("project.platform_name")) 33 | self.on_new_payload = self._platform.on_new_payload 34 | end 35 | 36 | 37 | function Platform:final() 38 | end 39 | 40 | 41 | ---@param callback function 42 | function Platform:login(callback) 43 | self._platform:login(callback) 44 | end 45 | 46 | 47 | ---@param user_id string 48 | function Platform:on_login(user_id) 49 | self._platform:on_login(user_id) 50 | end 51 | 52 | 53 | function Platform:get_payload() 54 | return self._platform:get_payload() 55 | end 56 | 57 | 58 | function Platform:consume_payload() 59 | self._platform:consume_payload() 60 | end 61 | 62 | 63 | function Platform:get_share_url() 64 | local match_key = app.profile:get_user_id() 65 | return self._platform:get_share_url(match_key) 66 | end 67 | 68 | 69 | function Platform:share_invite(share_url) 70 | if not share then 71 | return nil 72 | end 73 | 74 | share.text(eva.lang.txp("ui_share_text", share_url or self:get_share_url())) 75 | end 76 | 77 | 78 | ---@param text string 79 | ---@param on_success function|nil 80 | ---@param on_error function|nil 81 | function Platform:write_to_clipboard(text, on_success, on_error) 82 | return self._platform:write_to_clipboard(text, on_success, on_error) 83 | end 84 | 85 | 86 | function Platform:is_share_available() 87 | return self._platform:is_share_available() 88 | end 89 | 90 | 91 | ---@param platform_name string 92 | ---@return platform.base 93 | function Platform:_get_platform_class(platform_name) 94 | if html5 then 95 | platform_name = platform_name or "web" 96 | else 97 | platform_name = platform_name or "stub" 98 | end 99 | 100 | if platform_name == "android" then 101 | if not gpgs or not gpgs.is_supported() then 102 | platform_name = "stub" 103 | end 104 | end 105 | 106 | logger:debug("Init platform", { name = platform_name }) 107 | return PLATFORM_MAP[platform_name](platform_name, self.data) 108 | end 109 | 110 | 111 | return Platform 112 | -------------------------------------------------------------------------------- /game/system/game.render_script: -------------------------------------------------------------------------------- 1 | 2 | local rendercam = require("rendercam.rendercam") 3 | local vp = rendercam.viewport 4 | 5 | local IDENTITY_MATRIX = vmath.matrix4() 6 | local CLEAR_COLOR = hash("clear_color") 7 | local WINDOW_RESIZED = hash("window_resized") 8 | local UPDATE_WINDOW = hash("update window") 9 | 10 | 11 | local function update_window(self) 12 | rendercam.update_window(render.get_window_width(), render.get_window_height()) 13 | self.gui_proj = vmath.matrix4_orthographic(0, rendercam.window.x, 0, rendercam.window.y, -1, 1) 14 | end 15 | 16 | function init(self) 17 | self.tile_pred = render.predicate({"tile"}) 18 | self.gui_pred = render.predicate({"gui"}) 19 | self.world_gui_pred = render.predicate({"world_gui"}) 20 | self.text_pred = render.predicate({"text"}) 21 | self.model_pred = render.predicate({"model"}) 22 | self.particle_pred = render.predicate({"particle"}) 23 | 24 | self.clear_color = vmath.vector4(0) 25 | 26 | rendercam.configWin.x = render.get_width(); rendercam.configWin.y = render.get_height() 27 | rendercam.update_window_size(render.get_window_width(), render.get_window_height()) 28 | update_window(self) 29 | end 30 | 31 | function update(self) 32 | render.set_depth_mask(true) 33 | render.set_stencil_mask(0xff) 34 | render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0}) 35 | 36 | render.set_viewport(vp.x, vp.y, vp.width, vp.height) 37 | 38 | render.set_view(rendercam.calculate_view()) 39 | render.set_projection(rendercam.calculate_proj()) 40 | 41 | -- Sprite and particle rendering 42 | render.set_depth_mask(false) 43 | render.disable_state(render.STATE_DEPTH_TEST) 44 | render.disable_state(render.STATE_STENCIL_TEST) 45 | render.enable_state(render.STATE_BLEND) 46 | render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA) 47 | render.disable_state(render.STATE_CULL_FACE) 48 | 49 | render.draw(self.tile_pred) 50 | render.draw(self.particle_pred) 51 | --render.draw(self.world_gui_pred) 52 | 53 | -- Model rendering 54 | --render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA) 55 | --render.enable_state(render.STATE_CULL_FACE) 56 | --render.enable_state(render.STATE_DEPTH_TEST) 57 | --render.set_depth_mask(true) 58 | --render.draw(self.model_pred) 59 | 60 | -- Debug rendering - physics debug, draw_line 61 | --render.disable_state(render.STATE_DEPTH_TEST) 62 | --render.disable_state(render.STATE_CULL_FACE) 63 | --render.draw_debug3d() 64 | 65 | -- GUI Rendering 66 | render.set_viewport(0, 0, rendercam.window.x, rendercam.window.y) 67 | render.set_view(IDENTITY_MATRIX) 68 | render.set_projection(self.gui_proj) -- gui_proj only calculated on update_window 69 | 70 | render.enable_state(render.STATE_STENCIL_TEST) 71 | render.draw(self.gui_pred) 72 | render.draw(self.text_pred) -- Includes debug text from "draw_text" messages. 73 | render.disable_state(render.STATE_STENCIL_TEST) 74 | end 75 | 76 | function on_message(self, message_id, message) 77 | if message_id == CLEAR_COLOR then 78 | self.clear_color = message.color 79 | elseif message_id == WINDOW_RESIZED then -- sent by engine 80 | update_window(self) 81 | elseif message_id == UPDATE_WINDOW then -- sent by rendercam when a camera is activated ("window_resized" engine message requires data) 82 | update_window(self) 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /eva/resources/evadata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package evadata; 3 | 4 | message TokenRestoreConfig { 5 | uint32 timer = 1; 6 | int32 value = 2; 7 | int32 max = 3; 8 | } 9 | 10 | message TokenConfig { 11 | message TokenConfigData { 12 | int32 default = 1; 13 | string name = 2; 14 | int32 min = 3; 15 | int32 max = 4 [default = 2147483647]; 16 | } 17 | map token_config = 1; 18 | } 19 | 20 | 21 | message Tokens { 22 | message Token { 23 | string token_id = 1; 24 | int32 amount = 2; 25 | } 26 | repeated Token tokens = 1; 27 | } 28 | 29 | 30 | message TokenGroups { 31 | map token_groups = 1; 32 | } 33 | 34 | 35 | message Lots { 36 | message Lot { 37 | string price = 1; // TokenGroup ID 38 | string reward = 2; // TokenGroup ID 39 | } 40 | map token_lots = 1; 41 | } 42 | 43 | 44 | message Offers { 45 | message Offer { 46 | string category = 1; 47 | uint32 time = 2; 48 | string iap_id = 3; 49 | string lot_id = 4; 50 | } 51 | map offers = 1; 52 | } 53 | 54 | 55 | message IapsConfig { 56 | message IapConfig { 57 | string ident = 1; 58 | float price = 2; 59 | bool forever = 3; 60 | string token_group_id = 4; 61 | string category = 5; 62 | } 63 | map iaps = 1; 64 | } 65 | 66 | 67 | message Festivals { 68 | message Festival { 69 | string start_date = 1; 70 | uint32 duration = 2; 71 | string repeat_time = 3; 72 | uint32 close_time = 4; 73 | string category = 5; 74 | } 75 | map festivals = 1; 76 | } 77 | 78 | 79 | message Trucks { 80 | message Truck { 81 | uint32 lifetime = 1; 82 | uint32 cooldown = 2; 83 | bool autoarrive = 3; 84 | bool autoleave = 4; 85 | } 86 | map trucks = 1; 87 | } 88 | 89 | 90 | message Quests { 91 | message Quest { 92 | message QuestTasks { 93 | int32 initial = 1; 94 | int32 required = 2; 95 | string action = 3; 96 | string object = 4; 97 | string param1 = 5; 98 | string param2 = 6; 99 | } 100 | repeated string required_quests = 1; 101 | Tokens required_tokens = 2; 102 | repeated QuestTasks tasks = 3; 103 | Tokens reward = 4; 104 | string category = 5; 105 | bool events_offline = 6; 106 | bool autostart = 7; 107 | bool autofinish = 8; 108 | bool repeatable = 9; 109 | bool use_max_task_value = 10; 110 | } 111 | map quests = 1; 112 | } 113 | 114 | 115 | message Skills { 116 | message Skill { 117 | uint32 cast_time = 1; 118 | uint32 duration = 2; 119 | uint32 cooldown = 3; 120 | uint32 max_stack = 4 [default = 1]; 121 | uint32 restore_amount = 5 [default = 1]; 122 | bool channel = 6; 123 | bool manual_time = 7; 124 | } 125 | map skills = 1; 126 | } 127 | 128 | 129 | message Promocodes { 130 | message Promocode { 131 | Tokens tokens = 1; 132 | string start_date = 2; 133 | string end_date = 3; 134 | } 135 | map promocodes = 1; 136 | } 137 | 138 | 139 | message Ads { 140 | message AdSettings { 141 | string type = 1; 142 | uint32 time_between_shows = 2; 143 | uint32 time_from_game_start = 3; 144 | uint32 time_between_shows_all = 4; 145 | uint32 daily_limit = 5; 146 | uint32 all_ads_daily_limit = 6; 147 | string required_token_group = 7; 148 | } 149 | map ads = 1; 150 | } 151 | -------------------------------------------------------------------------------- /eva/modules/events.lua: -------------------------------------------------------------------------------- 1 | --- Eva events module 2 | -- Send all game events and can include different event systems 3 | -- If subscribe on event, you should call eva.events.on_message to 4 | -- like systems for analytics and game-logic 5 | -- @submodule eva 6 | 7 | 8 | local app = require("eva.app") 9 | local log = require("eva.log") 10 | local Event = require("eva.event") 11 | 12 | local logger = log.get_logger("eva.events") 13 | 14 | local M = {} 15 | 16 | 17 | --- Throws the game event 18 | -- @function eva.events.event 19 | -- @tparam string event name of event 20 | -- @tparam[opt={}] table params params 21 | function M.event(event_name, params) 22 | logger:debug("Event", { event = event_name, params = params }) 23 | if app.event_listeners[event_name] then 24 | app.event_listeners[event_name]:trigger(params) 25 | end 26 | end 27 | 28 | 29 | --- Setup current game screen 30 | -- @function eva.events.screen 31 | -- @tparam string screen_id screen id 32 | function M.screen(screen_id) 33 | 34 | end 35 | 36 | 37 | --- Subscribe the callback on event 38 | -- @function eva.events.subscribe 39 | -- @tparam string event_name Event name 40 | -- @tparam function callback Event callback 41 | -- @tparam table callback_context The first param for callback on fire 42 | function M.subscribe(event_name, callback, callback_context) 43 | app.event_listeners[event_name] = app.event_listeners[event_name] or Event() 44 | 45 | if M.is_subscribed(event_name, callback, callback_context) then 46 | logger:warn("The callback is already add to events. Aborting", { event_name = event_name }) 47 | return 48 | end 49 | 50 | app.event_listeners[event_name]:subscribe(callback, callback_context) 51 | end 52 | 53 | 54 | --- Subscribe the pack of events by map 55 | -- @function eva.events.subscribe_map 56 | -- @tparam table map {Event = Callback} map 57 | function M.subscribe_map(map, callback_context) 58 | for event_name, callback in pairs(map) do 59 | M.subscribe(event_name, callback, callback_context) 60 | end 61 | end 62 | 63 | 64 | --- Unsubscribe the event from events flow 65 | -- @function eva.events.unsubscribe 66 | -- @tparam string event_name Event name 67 | -- @tparam function callback Event callback 68 | function M.unsubscribe(event_name, callback, callback_context) 69 | if not M.is_subscribed(event_name, callback, callback_context) then 70 | logger:warn("No event to unsubscribe", { event_name = event_name }) 71 | end 72 | 73 | app.event_listeners[event_name]:unsubscribe(callback, callback_context) 74 | end 75 | 76 | 77 | --- Unsubscribe the pack of events by map 78 | -- @function eva.events.unsubscribe_map 79 | -- @tparam table map {Event = Callback} map 80 | function M.unsubscribe_map(map, callback_context) 81 | for event_name, callback in pairs(map) do 82 | M.unsubscribe(event_name, callback, callback_context) 83 | end 84 | end 85 | 86 | 87 | --- Check if callback is already subscribed 88 | -- @function eva.events.is_subscribed 89 | -- @tparam string event_name Event name 90 | -- @tparam function callback Event callback 91 | function M.is_subscribed(event_name, callback, callback_context) 92 | app.event_listeners[event_name] = app.event_listeners[event_name] or Event() 93 | return app.event_listeners[event_name]:is_subscribed(callback, callback_context) 94 | end 95 | 96 | 97 | function M.before_eva_init() 98 | app.event_listeners = {} 99 | end 100 | 101 | 102 | return M 103 | -------------------------------------------------------------------------------- /eva/modules/proto.lua: -------------------------------------------------------------------------------- 1 | --- Eva proto module 2 | -- Load proto file and give API to work with them 3 | -- @submodule eva 4 | 5 | 6 | local app = require("eva.app") 7 | local log = require("eva.log") 8 | local protoc = require("pb.protoc") 9 | 10 | local logger = log.get_logger("eva.proto") 11 | 12 | local M = {} 13 | 14 | 15 | local OPTIONAL = "optional" 16 | local REPEATED = "repeated" 17 | local MESSAGE = "message" 18 | local VALUE = "value" 19 | local MAP = "map" 20 | 21 | 22 | local function update_with_default_messages(proto_type, data) 23 | for name in pb.fields(proto_type) do 24 | local _, _, type, _, label = pb.field(proto_type, name) 25 | local _, _, field_type = pb.type(type) 26 | 27 | if field_type == MESSAGE then 28 | -- Nest messages 29 | if label == OPTIONAL then 30 | if not data[name] then 31 | data[name] = M.get(type) 32 | else 33 | data[name] = M.decode(type, M.encode(type, data[name])) 34 | end 35 | end 36 | 37 | -- Repeated list 38 | if label == REPEATED then 39 | for k, v in pairs(data[name]) do 40 | data[name][k] = M.decode(type, M.encode(type, v)) 41 | end 42 | end 43 | end 44 | 45 | -- Repeated map (key-value) 46 | -- In repeated map we should get value inside Entry 47 | if field_type == MAP then 48 | if label == REPEATED then 49 | for k, v in pairs(data[name]) do 50 | local _, _, repeated_type = pb.field(type, VALUE) 51 | local _, _, field_repeated_type = pb.type(repeated_type) 52 | 53 | if field_repeated_type == MESSAGE then 54 | data[name][k] = M.decode(repeated_type, M.encode(repeated_type, v)) 55 | end 56 | end 57 | end 58 | end 59 | end 60 | end 61 | 62 | 63 | --- Get empty template from proto type 64 | -- @function eva.proto.get 65 | -- @tparam string proto_type name of proto message e.g. 'eva.Token' 66 | -- @treturn table empty table with default values from proto 67 | function M.get(proto_type) 68 | return M.decode(proto_type) 69 | end 70 | 71 | 72 | --- Encode protobuf 73 | -- @function eva.proto.encode 74 | function M.encode(proto_type, data) 75 | return pb.encode(proto_type, data) 76 | end 77 | 78 | 79 | --- Decode protobuf 80 | -- @function eva.proto.decode 81 | function M.decode(proto_type, bytes) 82 | local settings = app.settings.proto 83 | local data = pb.decode(proto_type, bytes) 84 | 85 | -- Fill nested messages with default values 86 | -- Add default fields even in repeated and map fields 87 | if settings.default_nest_messages then 88 | update_with_default_messages(proto_type, data) 89 | end 90 | 91 | return data 92 | end 93 | 94 | 95 | --- Check data to match the proto_type 96 | -- Return data with default values according to proto_type 97 | -- @function eva.proto.verify 98 | -- @tparam string proto_type The prototype name 99 | -- @tparam table data The user data 100 | function M.verify(proto_type, data) 101 | return M.decode(proto_type, M.encode(proto_type, data)) 102 | end 103 | 104 | 105 | function M.before_eva_init() 106 | local settings = app.settings.proto 107 | local proto_paths = settings.proto_paths 108 | 109 | protoc.include_imports = true 110 | for path, values in pairs(proto_paths) do 111 | protoc:addpath(path) 112 | for _, proto in ipairs(values) do 113 | logger:debug("Load protofile", { path = path .. "/" .. proto }) 114 | protoc:loadfile(proto) 115 | end 116 | end 117 | end 118 | 119 | 120 | return M 121 | -------------------------------------------------------------------------------- /resources/evadata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package evadata; 3 | 4 | message TokenRestoreConfig { 5 | uint32 timer = 1; 6 | int32 value = 2; 7 | int32 max = 3; 8 | } 9 | 10 | message TokenConfig { 11 | message TokenConfigData { 12 | int32 default = 1; 13 | string name = 2; 14 | int32 min = 3; 15 | int32 max = 4 [default = 2147483647]; 16 | string category = 5; 17 | string token_image = 6; 18 | string token_name = 7; 19 | } 20 | map token_config = 1; 21 | } 22 | 23 | 24 | message Tokens { 25 | message Token { 26 | string token_id = 1; 27 | int32 amount = 2; 28 | } 29 | repeated Token tokens = 1; 30 | } 31 | 32 | 33 | message TokenGroups { 34 | map token_groups = 1; 35 | } 36 | 37 | 38 | message Lots { 39 | message Lot { 40 | string price = 1; // TokenGroup ID 41 | string reward = 2; // TokenGroup ID 42 | } 43 | map token_lots = 1; 44 | } 45 | 46 | 47 | message Offers { 48 | message Offer { 49 | string category = 1; 50 | uint32 time = 2; 51 | string iap_id = 3; 52 | string lot_id = 4; 53 | } 54 | map offers = 1; 55 | } 56 | 57 | 58 | message IapsConfig { 59 | message IapConfig { 60 | string ident = 1; 61 | float price = 2; 62 | bool forever = 3; 63 | string token_group_id = 4; 64 | string category = 5; 65 | } 66 | map iaps = 1; 67 | } 68 | 69 | 70 | message Festivals { 71 | message Festival { 72 | string start_date = 1; 73 | uint32 duration = 2; 74 | string repeat_time = 3; 75 | uint32 close_time = 4; 76 | string category = 5; 77 | } 78 | map festivals = 1; 79 | } 80 | 81 | 82 | message Trucks { 83 | message Truck { 84 | uint32 lifetime = 1; 85 | uint32 cooldown = 2; 86 | bool autoarrive = 3; 87 | bool autoleave = 4; 88 | } 89 | map trucks = 1; 90 | } 91 | 92 | 93 | message Quests { 94 | message Quest { 95 | message QuestTasks { 96 | int32 initial = 1; 97 | int32 required = 2; 98 | string action = 3; 99 | string object = 4; 100 | string param1 = 5; 101 | string param2 = 6; 102 | } 103 | repeated string required_quests = 1; 104 | Tokens required_tokens = 2; 105 | repeated QuestTasks tasks = 3; 106 | Tokens reward = 4; 107 | string category = 5; 108 | bool events_offline = 6; 109 | bool autostart = 7; 110 | bool autofinish = 8; 111 | bool repeatable = 9; 112 | bool use_max_task_value = 10; 113 | } 114 | map quests = 1; 115 | } 116 | 117 | 118 | message Skills { 119 | message Skill { 120 | uint32 cast_time = 1; 121 | uint32 duration = 2; 122 | uint32 cooldown = 3; 123 | uint32 max_stack = 4 [default = 1]; 124 | uint32 restore_amount = 5 [default = 1]; 125 | bool channel = 6; 126 | bool manual_time = 7; 127 | } 128 | map skills = 1; 129 | } 130 | 131 | 132 | message Promocodes { 133 | message Promocode { 134 | Tokens tokens = 1; 135 | string start_date = 2; 136 | string end_date = 3; 137 | } 138 | map promocodes = 1; 139 | } 140 | 141 | 142 | message Ads { 143 | message AdSettings { 144 | string type = 1; 145 | uint32 time_between_shows = 2; 146 | uint32 time_from_game_start = 3; 147 | uint32 time_between_shows_all = 4; 148 | uint32 daily_limit = 5; 149 | uint32 all_ads_daily_limit = 6; 150 | string required_token_group = 7; 151 | } 152 | map ads = 1; 153 | } 154 | -------------------------------------------------------------------------------- /eva/modules/promocode.lua: -------------------------------------------------------------------------------- 1 | --- Eva promocode module 2 | -- Can apply tokens or other stuff by code 3 | -- @submodule eva 4 | 5 | 6 | local app = require("eva.app") 7 | local luax = require("eva.luax") 8 | local const = require("eva.const") 9 | local time_string = require("eva.libs.time_string") 10 | 11 | local db = require("eva.modules.db") 12 | local game = require("eva.modules.game") 13 | local proto = require("eva.modules.proto") 14 | local saver = require("eva.modules.saver") 15 | local events = require("eva.modules.events") 16 | local wallet = require("eva.modules.wallet") 17 | 18 | local M = {} 19 | 20 | 21 | local function get_config() 22 | return db.get("Promocodes").promocodes 23 | end 24 | 25 | 26 | --- Get list of all redeemed codes 27 | -- @function eva.promocode.get_applied_codes 28 | -- @treturn string[] List of applied codes 29 | function M.get_applied_codes() 30 | return app[const.EVA.PROMOCODES].applied 31 | end 32 | 33 | 34 | --- Check if promocode is already applied 35 | -- @function eva.promocode.is_applied 36 | -- @tparam string code The promocode itself 37 | -- @treturn bool True if code is already redeemed 38 | function M.is_applied(code) 39 | return luax.table.contains(app[const.EVA.PROMOCODES].applied, code) 40 | end 41 | 42 | 43 | --- Try redeem the code and get rewards 44 | -- @function eva.promocode.redeem_code 45 | -- @tparam string code The promocode itself 46 | -- @treturn bool Result of success 47 | function M.redeem_code(code) 48 | if M.is_can_redeem(code) then 49 | wallet.add_many(get_config()[code].tokens) 50 | if app.promocode_settings.on_code_redeem then 51 | app.promocode_settings.on_code_redeem(code) 52 | end 53 | 54 | table.insert(app[const.EVA.PROMOCODES].applied, code) 55 | events.event(const.EVENT.CODE_REDEEM, { code = code }) 56 | return true 57 | end 58 | 59 | return false 60 | end 61 | 62 | 63 | --- Check if promocode can be redeem 64 | -- @function eva.promocode.is_can_redeem 65 | -- @tparam string code The promocode itself 66 | -- @treturn bool True of false 67 | function M.is_can_redeem(code) 68 | local config = get_config() 69 | 70 | if not code or not config[code] then 71 | return false 72 | end 73 | 74 | local start_date = config[code].start_date 75 | local end_date = config[code].end_date 76 | 77 | local is_date_valid = true 78 | if start_date ~= luax.string.empty and end_date ~= luax.string.empty then 79 | local current_time = game.get_time() 80 | local start_time = time_string.parse_ISO(start_date) 81 | local end_time = time_string.parse_ISO(end_date) 82 | is_date_valid = (start_time <= current_time) and (current_time <= end_time) 83 | end 84 | 85 | local is_applied = M.is_applied(code) 86 | local is_custom_ok = true 87 | if app.promocode_settings.is_can_code_redeem then 88 | is_custom_ok = app.promocode_settings.is_can_code_redeem(code) 89 | end 90 | 91 | return is_date_valid and not is_applied and is_custom_ok 92 | end 93 | 94 | 95 | --- Set promocode settings. 96 | -- Pass settings in eva.init function 97 | -- @function eva.promocode.set_settings 98 | function M.set_settings(promocode_settings) 99 | app.promocode_settings = promocode_settings 100 | end 101 | 102 | 103 | 104 | function M.on_eva_init() 105 | app.promocode_settings = {} 106 | app[const.EVA.PROMOCODES] = proto.get(const.EVA.PROMOCODES) 107 | saver.add_save_part(const.EVA.PROMOCODES, app[const.EVA.PROMOCODES]) 108 | end 109 | 110 | 111 | return M 112 | -------------------------------------------------------------------------------- /game/server/ServerRequester.lua: -------------------------------------------------------------------------------- 1 | local log = require("eva.log") 2 | local luax = require("eva.luax") 3 | local class = require("eva.libs.middleclass") 4 | local app = require("eva.app") 5 | local eva = require("eva.eva") 6 | local nakama = require("nakama.nakama") 7 | 8 | local const = require("game.const") 9 | 10 | local logger = log.get_logger("requester") 11 | 12 | 13 | ---@class ServerRequester 14 | ---@field _server Server 15 | local ServerRequester = class("server_requester") 16 | 17 | 18 | ---@param server Server 19 | function ServerRequester:initialize(server) 20 | self._server = server 21 | end 22 | 23 | 24 | function ServerRequester:finalize() 25 | end 26 | 27 | 28 | function ServerRequester:update_profile_data(display_name, avatar_id) 29 | local client = eva.server.get_client() 30 | nakama.update_account(client, { 31 | display_name = display_name, 32 | avatar_url = avatar_id 33 | }, luax.func.empty) 34 | end 35 | 36 | 37 | ---@return boolean Is success send message 38 | function ServerRequester:_socket_send(message, callback) 39 | local is_connected = eva.server.is_connected() 40 | if not is_connected then 41 | return false 42 | end 43 | 44 | local socket = eva.server.get_socket() 45 | local callback_index 46 | if callback then 47 | callback_index = eva.callbacks.create(callback) 48 | end 49 | nakama.socket_send(socket, message, function(response) 50 | if callback_index then 51 | eva.callbacks.call(callback_index, response) 52 | eva.callbacks.clear(callback_index) 53 | end 54 | end) 55 | 56 | return true 57 | end 58 | 59 | 60 | function ServerRequester:get_user_id_by_user_code(user_code, callback) 61 | nakama.sync(function() 62 | local objects = {{ 63 | collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, 64 | key = user_code 65 | }} 66 | 67 | local result = nakama.read_storage_objects(eva.server.get_client(), objects) 68 | if not result.error then 69 | local user_data = result.objects and result.objects[1] 70 | local user_id = json.decode(user_data.value).value 71 | if callback then 72 | callback(user_id) 73 | end 74 | end 75 | end) 76 | end 77 | 78 | 79 | ---@param callback func 80 | function ServerRequester:load_user_code(callback) 81 | nakama.sync(function() 82 | local objects = {{ 83 | collection = const.SERVER_STORAGE.USER_CODE_COLLECTION, 84 | key = const.SERVER_STORAGE.USER_CODE_KEY, 85 | userId = app.profile:get_user_id() 86 | }} 87 | 88 | local result = nakama.read_storage_objects(eva.server.get_client(), objects) 89 | if not result.error then 90 | local user_data = result.objects and result.objects[1] 91 | if user_data then 92 | local user_code = json.decode(user_data.value).value 93 | app.profile:set_user_code(user_code) 94 | if callback then 95 | callback(user_code) 96 | end 97 | end 98 | end 99 | end) 100 | end 101 | 102 | 103 | function ServerRequester:follow_users(user_ids, callback) 104 | if not user_ids then 105 | return 106 | end 107 | 108 | local message = nakama.create_status_follow_message(user_ids) 109 | self:_socket_send(message, function(response) 110 | local presences = response.status.presences or {} 111 | if callback then 112 | callback(presences) 113 | end 114 | end) 115 | end 116 | 117 | 118 | function ServerRequester:set_status(status) 119 | local message = nakama.create_status_update_message(status) 120 | self:_socket_send(message) 121 | end 122 | 123 | 124 | return ServerRequester 125 | -------------------------------------------------------------------------------- /eva/modules/labels.lua: -------------------------------------------------------------------------------- 1 | --- Eva labels module 2 | -- Add labels to the player to separate players on groups 3 | -- @submodule eva 4 | 5 | 6 | local app = require("eva.app") 7 | local luax = require("eva.luax") 8 | local const = require("eva.const") 9 | 10 | local ads = require("eva.modules.ads") 11 | local iaps = require("eva.modules.iaps") 12 | local game = require("eva.modules.game") 13 | local proto = require("eva.modules.proto") 14 | local saver = require("eva.modules.saver") 15 | local events = require("eva.modules.events") 16 | local wallet = require("eva.modules.wallet") 17 | 18 | local M = {} 19 | 20 | 21 | local function get_labels_config() 22 | local config_name = app.settings.labels.config 23 | local config = app.settings.labels.config 24 | return config and config.labels or {} 25 | end 26 | 27 | 28 | local spec_values_handlers = { 29 | ["ltv"] = iaps.get_ltv, 30 | ["maxpay"] = iaps.get_max_payment, 31 | ["ads"] = ads.get_ads_watched, 32 | ["days"] = game.get_days_played, 33 | } 34 | 35 | local function get_spec_key_value(key) 36 | return spec_values_handlers[key]() 37 | end 38 | 39 | 40 | local function check_spec_condition(cond) 41 | if not cond then 42 | return true 43 | end 44 | 45 | local result = true 46 | for i = 1, #cond do 47 | local data = cond[i] 48 | local key = data[1] 49 | local op = data[2] 50 | local value = data[3] 51 | local key_value = get_spec_key_value(key) 52 | 53 | result = result and luax.operators[op](key_value, value) 54 | end 55 | 56 | return result 57 | end 58 | 59 | 60 | local function check_token_condition(cond) 61 | if not cond then 62 | return true 63 | end 64 | 65 | local result = true 66 | for i = 1, #cond do 67 | local data = cond[i] 68 | local key = data[1] 69 | local op = data[2] 70 | local value = data[3] 71 | local key_value = wallet.get(key) 72 | 73 | result = result and luax.operators[op](key_value, value) 74 | end 75 | 76 | return result 77 | end 78 | 79 | 80 | local function check_conditions(label_data) 81 | local condition = true 82 | 83 | local spec = label_data.spec_cond 84 | condition = condition and check_spec_condition(spec) 85 | 86 | local token = label_data.token_cond 87 | condition = condition and check_token_condition(token) 88 | 89 | return condition 90 | end 91 | 92 | 93 | local function check_label(label_id, label_data) 94 | local is_need = check_conditions(label_data) 95 | local is_exist = M.is_exist(label_id) 96 | 97 | if is_exist and not is_need then 98 | luax.table.remove_item(app[const.EVA.LABELS].labels, label_id) 99 | events.event(const.EVENT.LABEL_REMOVE, { label_id = label_id }) 100 | end 101 | 102 | if not is_exist and is_need then 103 | table.insert(app[const.EVA.LABELS].labels, label_id) 104 | events.event(const.EVENT.LABEL_ADD, { label_id = label_id }) 105 | end 106 | 107 | end 108 | 109 | 110 | local function update_labels() 111 | local labels_data = get_labels_config() 112 | for id, data in pairs(labels_data) do 113 | check_label(id, data) 114 | end 115 | end 116 | 117 | 118 | function M.on_eva_init() 119 | app[const.EVA.LABELS] = proto.get(const.EVA.LABELS) 120 | saver.add_save_part(const.EVA.LABELS, app[const.EVA.LABELS]) 121 | end 122 | 123 | 124 | function M.after_eva_init() 125 | update_labels() 126 | end 127 | 128 | 129 | --- Check label is exist in player profile 130 | -- @function eva.labels.is_exist 131 | -- @tparam string label The label id 132 | -- @treturn bool True, if label in player profile 133 | function M.is_exist(label) 134 | local labels = app[const.EVA.LABELS].labels 135 | return luax.table.contains(labels, label) 136 | end 137 | 138 | 139 | return M 140 | -------------------------------------------------------------------------------- /game/settings/window_settings.lua: -------------------------------------------------------------------------------- 1 | --- Window settings example 2 | -- default settings will be extend with windows custom 3 | -- every settings should have next field: 4 | -- - render_order - number 0..15 to gui.set_render_order 5 | -- - appear_func - function(cb) on window show 6 | -- - disappear_func - function(cb) on window close 7 | -- - before_show_scene - function before show scene 8 | -- - after_show_scene - function after show scene 9 | -- - before_show_window - function before show window 10 | -- - after_show_window - function after show window 11 | -- callback should be executed always! 12 | -- @local 13 | 14 | local eva = require("eva.eva") 15 | local luax = require("eva.luax") 16 | local const = require("game.const") 17 | 18 | 19 | local function appear_simple(settings, cb) 20 | local shadow = gui.get_node("window_shadow/root") 21 | local target_alpha = gui.get_alpha(shadow) 22 | local root = gui.get_node("root") 23 | local root_scale = gui.get_scale(root) 24 | 25 | luax.gui.set_alpha(shadow, 0) 26 | gui.animate(shadow, luax.gui.PROP_ALPHA, target_alpha, gui.EASING_OUTSINE, 0.25, 0, cb) 27 | 28 | luax.gui.set_alpha(root, 0) 29 | gui.set_scale(root, vmath.vector3(0.7)) 30 | gui.animate(root, gui.PROP_SCALE, root_scale, gui.EASING_OUTCUBIC, 0.3) 31 | gui.animate(root, luax.gui.PROP_ALPHA, 1, gui.EASING_INCUBIC, 0.15) 32 | 33 | local root_position = gui.get_position(root) 34 | luax.gui.add_y(root, 20) 35 | gui.animate(root, luax.gui.PROP_POS_Y, root_position.y, gui.EASING_OUTSINE, 0.3) 36 | end 37 | 38 | 39 | local function disappear_simple(settings, cb) 40 | local shadow = gui.get_node("window_shadow/root") 41 | local root = gui.get_node("root") 42 | 43 | gui.animate(shadow, luax.gui.PROP_ALPHA, 0, gui.EASING_OUTCUBIC, 0.10, 0.10, cb) 44 | 45 | gui.animate(root, gui.PROP_SCALE, 0.7, gui.EASING_INCUBIC, 0.35) 46 | gui.animate(root, luax.gui.PROP_ALPHA, 0, gui.EASING_INSINE, 0.15, 0.10) 47 | local root_position = gui.get_position(root) 48 | gui.animate(root, luax.gui.PROP_POS_Y, root_position.y + 20, gui.EASING_INSINE, 0.2) 49 | end 50 | 51 | 52 | local function before_show_scene(cb) 53 | local time = const.SCENE_TRANSITION_TIME 54 | msg.post(const.PATH.TRANSITION, const.MSG.TRANSITION, { is_enable = true, time = time }) 55 | timer.delay(time, false, cb) 56 | end 57 | 58 | 59 | local function after_show_scene(cb) 60 | local time = const.SCENE_TRANSITION_TIME 61 | msg.post(const.PATH.TRANSITION, const.MSG.TRANSITION, { is_enable = false, time = time }) 62 | if cb then 63 | cb() 64 | end 65 | end 66 | 67 | 68 | local function before_show_window(cb) 69 | cb() 70 | end 71 | 72 | 73 | local function after_show_window(cb) 74 | if cb then 75 | cb() 76 | end 77 | end 78 | 79 | 80 | local M = { 81 | ["default"] = { 82 | render_order = 10, 83 | appear_func = appear_simple, 84 | disappear_func = disappear_simple, 85 | before_show_scene = before_show_scene, 86 | after_show_scene = after_show_scene, 87 | before_show_window = before_show_window, 88 | after_show_window = after_show_window, 89 | is_popup_on_popup = false, 90 | no_stack = false, 91 | }, 92 | 93 | [const.WINDOW.SCENE_MENU] = { 94 | render_order = 5 95 | }, 96 | 97 | [const.WINDOW.SCENE_GAME] = { 98 | render_order = 5 99 | }, 100 | 101 | [const.WINDOW.WINDOW_SETTINGS] = { 102 | render_order = 10 103 | }, 104 | 105 | [const.WINDOW.WINDOW_ADMIN] = { 106 | render_order = 10 107 | }, 108 | 109 | [const.WINDOW.WINDOW_CONFIRM] = { 110 | render_order = 13, 111 | is_popup_on_popup = true 112 | }, 113 | 114 | [const.WINDOW.WINDOW_INFO] = { 115 | render_order = 13, 116 | is_popup_on_popup = true 117 | } 118 | } 119 | 120 | return M 121 | -------------------------------------------------------------------------------- /bundle/manifests/game.appmanifest: -------------------------------------------------------------------------------- 1 | platforms: 2 | armv7-ios: 3 | context: 4 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 5 | excludeSymbols: [] 6 | symbols: [] 7 | libs: [physics_null, record_null, liveupdate_null] 8 | frameworks: [] 9 | linkFlags: [] 10 | arm64-ios: 11 | context: 12 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 13 | excludeSymbols: [] 14 | symbols: [] 15 | libs: [physics_null, record_null, liveupdate_null] 16 | frameworks: [] 17 | linkFlags: [] 18 | x86_64-ios: 19 | context: 20 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 21 | excludeSymbols: [] 22 | symbols: [] 23 | libs: [physics_null, record_null, liveupdate_null] 24 | frameworks: [] 25 | linkFlags: [] 26 | armv7-android: 27 | context: 28 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 29 | excludeJars: [] 30 | excludeSymbols: [] 31 | symbols: [] 32 | libs: [physics_null, record_null, liveupdate_null] 33 | linkFlags: [] 34 | arm64-android: 35 | context: 36 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 37 | excludeJars: [] 38 | excludeSymbols: [] 39 | symbols: [] 40 | libs: [physics_null, record_null, liveupdate_null] 41 | linkFlags: [] 42 | x86_64-osx: 43 | context: 44 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 45 | excludeSymbols: [] 46 | symbols: [] 47 | libs: [physics_null, record_null, liveupdate_null] 48 | frameworks: [] 49 | linkFlags: [] 50 | x86_64-linux: 51 | context: 52 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 53 | excludeSymbols: [] 54 | symbols: [] 55 | libs: [physics_null, record_null, liveupdate_null] 56 | linkFlags: [] 57 | x86-win32: 58 | context: 59 | excludeLibs: [libphysics, libLinearMath, libBulletDynamics, libBulletCollision, libBox2D, librecord, vpx, libliveupdate] 60 | excludeSymbols: [] 61 | symbols: [] 62 | libs: [libphysics_null.lib, librecord_null.lib, libliveupdate_null.lib] 63 | linkFlags: [] 64 | x86_64-win32: 65 | context: 66 | excludeLibs: [libphysics, libLinearMath, libBulletDynamics, libBulletCollision, libBox2D, librecord, vpx, libliveupdate] 67 | excludeSymbols: [] 68 | symbols: [] 69 | libs: [libphysics_null.lib, librecord_null.lib, libliveupdate_null.lib] 70 | linkFlags: [] 71 | js-web: 72 | context: 73 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 74 | excludeJsLibs: [] 75 | excludeSymbols: [] 76 | symbols: [] 77 | libs: [physics_null, record_null, liveupdate_null] 78 | linkFlags: [] 79 | wasm-web: 80 | context: 81 | excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, Box2D, record, vpx, liveupdate] 82 | excludeJsLibs: [] 83 | excludeSymbols: [] 84 | symbols: [] 85 | libs: [physics_null, record_null, liveupdate_null] 86 | linkFlags: [] 87 | -------------------------------------------------------------------------------- /eva/libs/smart/value.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | function M.set(self, value, reason, is_visual_later) 5 | if self.params.min then 6 | value = math.max(self.params.min, value) 7 | end 8 | 9 | if self.params.max then 10 | value = math.min(value, self.params.max) 11 | end 12 | 13 | local old_value = self:get() 14 | local delta = value - old_value 15 | 16 | self.data_table.amount = value - self.data_table.offset 17 | if delta > 0 then 18 | self.data_table.total_sum = self.data_table.total_sum + delta 19 | end 20 | 21 | if is_visual_later then 22 | self.visual_credit = self.visual_credit + (value - old_value) 23 | end 24 | 25 | if delta ~= 0 then 26 | if self._on_change_callbacks then 27 | for i = 1, #self._on_change_callbacks do 28 | self._on_change_callbacks[i](self, delta, reason) 29 | end 30 | end 31 | end 32 | 33 | return self:get() 34 | end 35 | 36 | 37 | function M.get(self) 38 | return self.data_table.amount + self.data_table.offset 39 | end 40 | 41 | 42 | function M.add(self, value, reason, is_visual_later) 43 | local prev_value = self:get() 44 | local new_value = self:set(prev_value + value, reason, is_visual_later) 45 | return new_value 46 | end 47 | 48 | 49 | function M.sync_visual(self) 50 | local prev_value = self.visual_credit 51 | self.visual_credit = 0 52 | 53 | return prev_value 54 | end 55 | 56 | 57 | function M.add_visual(self, value) 58 | self.visual_credit = self.visual_credit - value 59 | end 60 | 61 | 62 | function M.get_visual(self) 63 | return self:get() - self.visual_credit 64 | end 65 | 66 | 67 | function M.is_max(self) 68 | if self.params.max then 69 | return self:get() == self.params.max 70 | end 71 | return false 72 | end 73 | 74 | 75 | function M.get_max(self) 76 | return self.params.max 77 | end 78 | 79 | 80 | function M.is_any(self) 81 | return self:get() > 0 82 | end 83 | 84 | 85 | function M.is_empty(self) 86 | return self:get() == 0 87 | end 88 | 89 | 90 | function M.check(self, value) 91 | return self:get() >= value 92 | end 93 | 94 | 95 | function M.pay(self, value, reason, is_visual_later) 96 | value = value or 1 97 | 98 | if self:check(value) then 99 | return self:add(-value, reason, is_visual_later) 100 | end 101 | 102 | return false 103 | end 104 | 105 | 106 | function M.set_max(self) 107 | if self.params.max then 108 | self:set(self.params.max) 109 | end 110 | end 111 | 112 | 113 | function M.on_change(self, callback) 114 | self._on_change_callbacks = self._on_change_callbacks or {} 115 | table.insert(self._on_change_callbacks, callback) 116 | end 117 | 118 | 119 | function M.init(self, params, data_table) 120 | self.data_table = data_table 121 | self.params = params or {} 122 | 123 | if not data_table.amount then 124 | data_table.amount = (self.params.default or 0) - self.data_table.offset 125 | end 126 | self:set(data_table.amount) 127 | self:sync_visual() 128 | end 129 | 130 | 131 | --- Offset needed for protect from memory scanning 132 | -- Call random offset on window.focus for more protecting 133 | function M.random_offset(self) 134 | local prev_offset = self.data_table.offset 135 | self.data_table.offset = math.random(99, 9999) 136 | local diff = prev_offset - self.data_table.offset 137 | self.data_table.amount = self.data_table.amount + diff 138 | end 139 | 140 | 141 | --- Reset offset to 0 value. This will turn off 142 | -- memory protecting with value offsets 143 | function M.reset_offset(self) 144 | local prev_offset = self.data_table.offset 145 | self.data_table.offset = 0 146 | local diff = prev_offset 147 | self.data_table.amount = self.data_table.amount + diff 148 | end 149 | 150 | 151 | --- Return token_id from token.params.name 152 | function M.get_token_id(self) 153 | return self.params.name 154 | end 155 | 156 | 157 | return M 158 | -------------------------------------------------------------------------------- /eva/modules/ads/ads_yandex.lua: -------------------------------------------------------------------------------- 1 | --- Eva yandex ads plugin adapter 2 | -- @module adapter.yandex 3 | -- @local 4 | 5 | local log = require("eva.log") 6 | local const = require("eva.const") 7 | local yagames = const.require("yagames.yagames") 8 | 9 | local game = require("eva.modules.game") 10 | local device = require("eva.modules.device") 11 | local sound = require("eva.modules.sound") 12 | 13 | local logger = log.get_logger("ads.yandex") 14 | 15 | local Ads = {} 16 | Ads._on_ready_callback = nil 17 | Ads._sound_gain = nil 18 | Ads._music_gain = nil 19 | 20 | 21 | local function mute_sounds() 22 | Ads._music_gain = sound.get_music_gain() 23 | Ads._sound_gain = sound.get_sound_gain() 24 | sound.set_music_gain(0) 25 | sound.set_sound_gain(0) 26 | end 27 | 28 | 29 | local function return_sounds() 30 | sound.set_music_gain(Ads._music_gain) 31 | sound.set_sound_gain(Ads._sound_gain) 32 | end 33 | 34 | 35 | local function show_interstitial(ad_id, success_callback, finish_callback, error_callback) 36 | yagames.adv_show_fullscreen_adv({ 37 | open = function() 38 | logger:debug("Open interstitial", { ad_id = ad_id }) 39 | mute_sounds() 40 | end, 41 | close = function(self, was_shown) 42 | logger:debug("Close interstitial", { ad_id = ad_id }) 43 | return_sounds() 44 | if was_shown then 45 | finish_callback(ad_id) 46 | success_callback(ad_id) 47 | end 48 | end, 49 | offline = function() 50 | error_callback(ad_id) 51 | logger:debug("Offline interstitial", { ad_id = ad_id }) 52 | end, 53 | error = function() 54 | error_callback(ad_id) 55 | logger:debug("Error interstitial", { ad_id = ad_id }) 56 | end 57 | }) 58 | end 59 | 60 | 61 | local function show_rewarded(ad_id, success_callback, finish_callback, error_callback) 62 | yagames.adv_show_rewarded_video({ 63 | open = function() 64 | logger:debug("Open rewarded", { ad_id = ad_id }) 65 | mute_sounds() 66 | end, 67 | close = function() 68 | logger:debug("Close rewarded", { ad_id = ad_id }) 69 | return_sounds() 70 | end, 71 | rewarded = function() 72 | logger:debug("Success rewarded", { ad_id = ad_id }) 73 | finish_callback(ad_id) 74 | success_callback(ad_id) 75 | end, 76 | error = function() 77 | error_callback(ad_id) 78 | logger:debug("Error rewarded", { ad_id = ad_id }) 79 | end 80 | }) 81 | end 82 | 83 | 84 | --- Show ads 85 | -- @function adapter.show 86 | -- @tparam string ad_id 87 | -- @tparam table ad_config 88 | -- @tparam function success_callback The callback on ads success show 89 | -- @tparam function error_callback The callback on ads failure show 90 | -- @local 91 | function Ads.show(ad_id, ad_config, success_callback, finish_callback, error_callback) 92 | if (game.is_debug() and device.is_desktop()) or not yagames then 93 | finish_callback(ad_id) 94 | success_callback(ad_id) 95 | return 96 | end 97 | 98 | if ad_config.type == "rewardedVideo" then 99 | show_rewarded(ad_id, success_callback, finish_callback, error_callback) 100 | end 101 | if ad_config.type == "video" then 102 | show_interstitial(ad_id, success_callback, finish_callback, error_callback) 103 | end 104 | end 105 | 106 | 107 | --- Init the ads adapter 108 | -- @function adapter.initialize 109 | -- @tparam string project_id 110 | -- @tparam function on_ready_callback 111 | -- @local 112 | function Ads.initialize(project_id, on_ready_callback) 113 | Ads._on_ready_callback = on_ready_callback 114 | Ads._music_gain = sound.get_music_gain() 115 | Ads._sound_gain = sound.get_sound_gain() 116 | end 117 | 118 | 119 | --- Check if ads on adapter is ready 120 | -- @function adapter.is_ready 121 | -- @tparam string ads_id 122 | -- @tparam table ad_config 123 | -- @treturn boolean 124 | -- @local 125 | function Ads.is_ready(ad_id, ad_config) 126 | return true 127 | end 128 | 129 | 130 | return Ads 131 | -------------------------------------------------------------------------------- /gui/window_confirm/window_confirm.gui_script: -------------------------------------------------------------------------------- 1 | local druid = require("druid.druid") ---@type druid 2 | local druid_helper = require("druid.helper") 3 | 4 | local eva = require("eva.eva") ---@type eva 5 | local const = require("game.const") 6 | 7 | local WINDOW_ID = const.WINDOW.WINDOW_CONFIRM 8 | 9 | ---@class window.confirm 10 | ---@field data window.confirm_data 11 | 12 | ---@class window.confirm_data 13 | ---@field callbacks window.confirm_callbacks 14 | ---@field text string 15 | ---@field header string|nil 16 | ---@field button_ok_text string|nil 17 | ---@field button_cancel_text string|nil 18 | ---@field checkbox_text string|nil 19 | ---@field is_disable_close_button boolean 20 | 21 | ---@class window.confirm_callbacks 22 | ---@field on_cancel function 23 | ---@field on_agree function 24 | ---@field on_checkbox function 25 | 26 | 27 | ---@param self window.confirm 28 | local function close_window(self, callback) 29 | local callbacks = self.data.callbacks 30 | local is_checked = self.checkbox:get_state() 31 | eva.window.disappear(WINDOW_ID, function() 32 | if callbacks and callbacks.on_cancel then 33 | callbacks.on_cancel() 34 | end 35 | if callbacks and callbacks.on_checkbox and is_checked then 36 | callbacks.on_checkbox() 37 | end 38 | if callback then 39 | callback() 40 | end 41 | end) 42 | end 43 | 44 | 45 | ---@param self window.confirm 46 | local function close_with_agree(self) 47 | local callbacks = self.data.callbacks 48 | eva.window.disappear(WINDOW_ID, function() 49 | if callbacks and callbacks.on_agree then 50 | callbacks.on_agree() 51 | end 52 | end) 53 | end 54 | 55 | 56 | ---@param self window.confirm 57 | local function on_agree_button(self) 58 | close_with_agree(self) 59 | end 60 | 61 | 62 | ---@param self window.confirm 63 | function init(self) 64 | self.druid = druid.new(self) 65 | self.root = gui.get_node("root") 66 | self.data = eva.window.get_data(WINDOW_ID) 67 | 68 | self.druid:new_button("window_shadow/root", close_window) 69 | self.druid:new_blocker("content") 70 | self.druid:new_button("base_window/button_close/button", close_window) 71 | self.druid:new_button("button_agree/button", on_agree_button) 72 | self.druid:new_button("button_cancel/button", close_window) 73 | 74 | self.druid:new_text("base_window/text_header", self.data.header or eva.lang.txt("ui_header_confirm")) 75 | self.druid:new_text("button_agree/text", self.data.button_ok_text or eva.lang.txt("ui_agree")) 76 | self.druid:new_text("button_cancel/text", self.data.button_cancel_text or eva.lang.txt("ui_cancel")) 77 | self.druid:new_text("text_info", self.data.text) 78 | self.druid:new_text("text_checkbox", self.data.checkbox_text) 79 | 80 | self.checkbox = self.druid:new_checkbox("icon_check", self._on_checkbox_click, "checkbox_confirm") 81 | gui.set_enabled(gui.get_node("panel_checkbox"), self.data.checkbox_text) 82 | 83 | druid_helper.centrate_nodes(4, gui.get_node("text_checkbox"), gui.get_node("checkbox_confirm")) 84 | 85 | if self.data.is_disable_close_button then 86 | gui.set_enabled(gui.get_node("close_background"), false) 87 | gui.set_enabled(gui.get_node("button_close"), false) 88 | else 89 | self.druid:new_back_handler(close_window) 90 | end 91 | 92 | eva.window.appear(WINDOW_ID) 93 | end 94 | 95 | 96 | ---@param self window.confirm 97 | function final(self) 98 | self.druid:final() 99 | end 100 | 101 | 102 | ---@param self window.confirm 103 | function update(self, dt) 104 | self.druid:update(dt) 105 | end 106 | 107 | 108 | ---@param self window.confirm 109 | function on_message(self, message_id, message, sender) 110 | eva.window.on_message(WINDOW_ID, message_id, message, sender) 111 | self.druid:on_message(message_id, message, sender) 112 | end 113 | 114 | 115 | ---@param self window.confirm 116 | function on_input(self, action_id, action) 117 | return self.druid:on_input(action_id, action) 118 | end 119 | -------------------------------------------------------------------------------- /eva/modules/hexgrid/hexgrid_convertations.lua: -------------------------------------------------------------------------------- 1 | -- Convertation utils for hexgrids 2 | 3 | local app = require("eva.app") 4 | local luax = require("eva.luax") 5 | 6 | local M = {} 7 | 8 | 9 | function M.cell_to_pos_flattop(i, j, data) 10 | local part_size = data.tile.width - data.tile.side 11 | local two_hex_width = data.tile.width + data.tile.side 12 | 13 | local x = two_hex_width / 2 * i 14 | local y = data.tile.height * (j + 0.5 * (bit.band(i, 1))) 15 | 16 | -- invert 17 | if data.scene.invert_y then 18 | y = data.scene.size_y - y 19 | end 20 | 21 | -- add half offset 22 | x = x + part_size 23 | y = y + (data.scene.invert_y and -data.tile.height/2 or data.tile.height/2) 24 | 25 | return x, y 26 | end 27 | 28 | 29 | function M.cell_to_pos_pointytop(i, j, data) 30 | local part_size = data.tile.height - data.tile.side 31 | local two_hex_height = data.tile.height + data.tile.side 32 | 33 | local x = data.tile.width * (i + 0.5 * (bit.band(j, 1))) 34 | local y = two_hex_height / 2 * j 35 | 36 | -- invert 37 | if data.scene.invert_y then 38 | y = data.scene.size_y - y 39 | end 40 | 41 | -- add half offset 42 | x = x + data.tile.width/2 43 | y = y + (data.scene.invert_y and -part_size or part_size) 44 | 45 | return x, y 46 | end 47 | 48 | 49 | function M.pos_to_cell_flattop(x, y, map_params) 50 | local data = map_params or app.hexgrid_default 51 | 52 | local part_size = data.tile.width - data.tile.side 53 | local two_hex_width = data.tile.width + data.tile.side 54 | 55 | -- add half offset 56 | x = x - part_size 57 | y = y - (data.scene.invert_y and -data.tile.height/2 or data.tile.height/2) 58 | 59 | -- invert 60 | if data.scene.invert_y then 61 | y = data.scene.size_y - y 62 | end 63 | 64 | local i = luax.math.round(2 * x / two_hex_width) 65 | local j = luax.math.round(y / data.tile.height - 0.5 * bit.band(i, 1)) 66 | 67 | return i, j 68 | end 69 | 70 | 71 | function M.pos_to_cell_pointytop(x, y, map_params) 72 | local data = map_params or app.hexgrid_default 73 | 74 | local part_size = data.tile.height - data.tile.side 75 | local two_hex_height = data.tile.height + data.tile.side 76 | 77 | -- add half offset 78 | x = x - data.tile.width/2 79 | y = y - (data.scene.invert_y and -part_size or part_size) 80 | 81 | -- invert 82 | if data.scene.invert_y then 83 | y = data.scene.size_y - y 84 | end 85 | 86 | local j = luax.math.round(2 * y / two_hex_height) 87 | local i = luax.math.round(x / data.tile.width - 0.5 * bit.band(j, 1)) 88 | 89 | return i, j 90 | end 91 | 92 | 93 | function M.cell_cube_to_pos_pointytop(i, j, k, map_params) 94 | local offset_i, offset_j = M.cube_to_offset_pointytop(i, j, k, map_params) 95 | return M.cell_to_pos_pointytop(offset_i, offset_j, map_params) 96 | end 97 | 98 | 99 | function M.cell_cube_to_pos_flattop(i, j, k, map_params) 100 | local offset_i, offset_j = M.cube_to_offset_flattop(i, j, k, map_params) 101 | return M.cell_to_pos_flattop(offset_i, offset_j, map_params) 102 | end 103 | 104 | 105 | function M.pos_to_cell_cube_pointytop(x, y, map_params) 106 | local offset_i, offset_j = M.pos_to_cell_pointytop(x, y, map_params) 107 | return M.offset_to_cube_pointytop(offset_i, offset_j, map_params) 108 | end 109 | 110 | 111 | function M.pos_to_cell_cube_flattop(x, y, map_params) 112 | local offset_i, offset_j = M.pos_to_cell_flattop(x, y, map_params) 113 | return M.offset_to_cube_flattop(offset_i, offset_j, map_params) 114 | end 115 | 116 | 117 | function M.cube_to_offset_pointytop(i, j, k, map_params) 118 | return i + (k - bit.band(k, 1)) / 2, k 119 | end 120 | 121 | 122 | function M.cube_to_offset_flattop(i, j, k, map_params) 123 | return i, k + (i - bit.band(i, 1)) / 2 124 | end 125 | 126 | 127 | function M.offset_to_cube_pointytop(i, j, map_params) 128 | local x = i - (j - bit.band(j, 1)) / 2 129 | 130 | return x, -x - j, j 131 | end 132 | 133 | 134 | function M.offset_to_cube_flattop(i, j, map_params) 135 | local z = j - (i - bit.band(i, 1)) / 2 136 | 137 | return i, -i - z, z 138 | end 139 | 140 | 141 | return M 142 | --------------------------------------------------------------------------------