├── .buildpath ├── .gitignore ├── .project ├── .settings ├── .jsdtscope ├── org.eclipse.php.core.prefs ├── org.eclipse.wst.common.project.facet.core.xml ├── org.eclipse.wst.jsdt.ui.superType.container └── org.eclipse.wst.jsdt.ui.superType.name ├── .tern-project ├── LICENSE ├── README.md ├── dbmodel.sql ├── gameinfos.inc.php ├── gameoptions.inc.php ├── img ├── 110_110_dice.png ├── 25_25_wooden_tokens.png ├── 30_30_colored_dice.png ├── 30_30_meeple.png ├── 30_30_tokens.png ├── 30_30_wooden_cubes.png ├── 64_64_dice.jpg ├── 78_64_stand_meeples.png ├── castle_iso.png ├── castle_top.png ├── cube_iso.png ├── cube_top.png ├── cubes.png ├── cylinder_iso.png ├── cylinder_top.png ├── game_box.png ├── game_box180.png ├── game_box75.png ├── game_icon.png ├── house_iso.png ├── house_top.png ├── logo.png ├── meeple_iso.png ├── meeple_top.png └── publisher.png ├── material.inc.php ├── misc ├── NOTES.txt ├── bgaprojectrename.php ├── bgg_scrabber.php ├── game_util.php ├── gen_sprite.php ├── generate_state_diagram.php ├── genmat.php ├── genstate.php ├── mat_test.php ├── material.csv ├── measure.html ├── module │ ├── common │ │ └── deck.game.php │ └── table │ │ └── table.game.php ├── parser_trans.php ├── php.txt ├── sharedcode.test.php └── view │ └── common │ └── game.view.php ├── modules ├── APP_Extended.php ├── EuroGame.php ├── sharedparent.js └── tokens.php ├── sharedcode.action.php ├── sharedcode.css ├── sharedcode.game.php ├── sharedcode.js ├── sharedcode.view.php ├── sharedcode_sharedcode.tpl ├── states.inc.php ├── stats.inc.php ├── userscripts ├── bgabugredirect.user.js ├── bgalicensesorter.user.js └── bugcannedanswers.user.js └── version.php /.buildpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | misc/text.wiki 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | bga-sharedcode 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.common.project.facet.core.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.wst.validation.validationbuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.dltk.core.scriptbuilder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.php.core.PHPNature 26 | org.eclipse.wst.jsdt.core.jsNature 27 | org.eclipse.wst.common.project.facet.core.nature 28 | 29 | 30 | -------------------------------------------------------------------------------- /.settings/.jsdtscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.settings/org.eclipse.php.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | include_path=0;/bga-sharedcode\u00050;/bga-sharedcode/modules\u00050;/bga-sharedcode/misc 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.container: -------------------------------------------------------------------------------- 1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.name: -------------------------------------------------------------------------------- 1 | Window -------------------------------------------------------------------------------- /.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaVersion": 5, 3 | "plugins": { 4 | "guess-types": { 5 | 6 | }, 7 | "outline": { 8 | 9 | }, 10 | "dojotoolkit1.10": { 11 | 12 | } 13 | }, 14 | "libs": [ 15 | "ecma5", 16 | "browser" 17 | ] 18 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bga-sharedcode 2 | Mirror of bga sharedcode project - to share code and resource for bga developers 3 | -------------------------------------------------------------------------------- /dbmodel.sql: -------------------------------------------------------------------------------- 1 | 2 | -- ------ 3 | -- BGA framework: © Gregory Isabelli & Emmanuel Colin 4 | -- SharedCode implementation : © Alena Laskavaia 5 | -- 6 | -- This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 7 | -- See http://en.boardgamearena.com/#!doc/Studio for more information. 8 | -- ----- 9 | 10 | -- dbmodel.sql 11 | 12 | -- This is the file where you are describing the database schema of your game 13 | -- Basically, you just have to export from PhpMyAdmin your table structure and copy/paste 14 | -- this export here. 15 | -- Note that the database itself and the standard tables ("global", "stats", "gamelog" and "player") are 16 | -- already created and must not be created here 17 | 18 | -- Note: The database schema is created from this file when the game starts. If you modify this file, 19 | -- you have to restart a game to see your changes in database. 20 | 21 | 22 | 23 | 24 | 25 | -- Example 1: create a standard "card" table to be used with the "Deck" tools (see example game "hearts"): 26 | 27 | CREATE TABLE IF NOT EXISTS `card` ( 28 | `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 29 | `card_type` varchar(16) NOT NULL, 30 | `card_type_arg` int(11) NOT NULL, 31 | `card_location` varchar(16) NOT NULL, 32 | `card_location_arg` int(11) NOT NULL, 33 | PRIMARY KEY (`card_id`) 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 35 | 36 | -- Example 2: create a resource table, resource count for each player, 37 | -- we could have altered player table but it probably better to keep it separate 38 | 39 | CREATE TABLE IF NOT EXISTS `resource` ( 40 | `player_id` int(10) unsigned NOT NULL, 41 | `resource_key` varchar(32) NOT NULL, 42 | `resource_count` int(10) signed NOT NULL, 43 | PRIMARY KEY (`player_id`,`resource_key`) 44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 45 | 46 | ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id); 47 | 48 | 49 | -- Example 3: create a token table, simple table contain token_id as string, location as string and state as integer 50 | -- this is sufficient for most euro-games 51 | 52 | CREATE TABLE IF NOT EXISTS `token` ( 53 | `token_key` varchar(32) NOT NULL, 54 | `token_location` varchar(32) NOT NULL, 55 | `token_state` int(10), 56 | PRIMARY KEY (`token_key`) 57 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 58 | 59 | 60 | 61 | -- Example 5: add a custom field to the standard "player" table 62 | -- ALTER TABLE `player` ADD `player_my_custom_field` INT UNSIGNED NOT NULL DEFAULT '0'; 63 | -------------------------------------------------------------------------------- /gameinfos.inc.php: -------------------------------------------------------------------------------- 1 | 'Board Game Arena', 18 | 19 | // Game artist (or game artists, separated by commas) 20 | 'artist' => 'Board Game Arena', 21 | 22 | // Year of FIRST publication of this game. Can be negative. 23 | 'year' => 2017, 24 | 25 | // Game publisher 26 | 'publisher' => 'Board Game Arena', 27 | 28 | // Url of game publisher website 29 | 'publisher_website' => 'http://www.boardgamearena.com/', 30 | 31 | // Board Game Geek ID of the publisher 32 | 'publisher_bgg_id' => 1234, 33 | 34 | // Board game geek ID of the game 35 | 'bgg_id' => 54321, 36 | 37 | 38 | // Players configuration that can be played (ex: 2 to 4 players) 39 | 'players' => array( 1,2,3,4 ), 40 | 41 | // Suggest players to play with this number of players. Must be null if there is no such advice, or if there is only one possible player configuration. 42 | 'suggest_player_number' => null, 43 | 44 | // Discourage players to play with these numbers of players. Must be null if there is no such advice. 45 | 'not_recommend_player_number' => null, 46 | // 'not_recommend_player_number' => array( 2, 3 ), // <= example: this is not recommended to play this game with 2 or 3 players 47 | 48 | 49 | // Estimated game duration, in minutes (used only for the launch, afterward the real duration is computed) 50 | 'estimated_duration' => 30, 51 | 52 | // Time in second add to a player when "giveExtraTime" is called (speed profile = fast) 53 | 'fast_additional_time' => 30, 54 | 55 | // Time in second add to a player when "giveExtraTime" is called (speed profile = medium) 56 | 'medium_additional_time' => 40, 57 | 58 | // Time in second add to a player when "giveExtraTime" is called (speed profile = slow) 59 | 'slow_additional_time' => 50, 60 | 61 | // If you are using a tie breaker in your game (using "player_score_aux"), you must describe here 62 | // the formula used to compute "player_score_aux". This description will be used as a tooltip to explain 63 | // the tie breaker to the players. 64 | // Note: if you are NOT using any tie breaker, leave the empty string. 65 | // 66 | // Example: 'tie_breaker_description' => totranslate( "Number of remaining cards in hand" ), 67 | 'tie_breaker_description' => "", 68 | 69 | // Game is "beta". A game MUST set is_beta=1 when published on BGA for the first time, and must remains like this until all bugs are fixed. 70 | 'is_beta' => 1, 71 | 72 | // Is this game cooperative (all players wins together or loose together) 73 | 'is_coop' => 0, 74 | 75 | 76 | // Complexity of the game, from 0 (extremely simple) to 5 (extremely complex) 77 | 'complexity' => 3, 78 | 79 | // Luck of the game, from 0 (absolutely no luck in this game) to 5 (totally luck driven) 80 | 'luck' => 3, 81 | 82 | // Strategy of the game, from 0 (no strategy can be setup) to 5 (totally based on strategy) 83 | 'strategy' => 3, 84 | 85 | // Diplomacy of the game, from 0 (no interaction in this game) to 5 (totally based on interaction and discussion between players) 86 | 'diplomacy' => 3, 87 | 88 | // Colors attributed to players 89 | 'player_colors' => array( "ff0000", "008000", "0000ff", "ffa500", "773300" ), 90 | 91 | // Favorite colors support : if set to "true", support attribution of favorite colors based on player's preferences (see reattributeColorsBasedOnPreferences PHP method) 92 | 'favorite_colors_support' => true, 93 | 94 | // Game interface width range (pixels) 95 | // Note: game interface = space on the left side, without the column on the right 96 | 'game_interface_width' => array( 97 | 98 | // Minimum width 99 | // default: 740 100 | // maximum possible value: 740 (ie: your game interface should fit with a 740px width (correspond to a 1024px screen) 101 | // minimum possible value: 320 (the lowest value you specify, the better the display is on mobile) 102 | 'min' => 740, 103 | 104 | // Maximum width 105 | // default: null (ie: no limit, the game interface is as big as the player's screen allows it). 106 | // maximum possible value: unlimited 107 | // minimum possible value: 740 108 | 'max' => null 109 | ), 110 | 111 | // Games categories 112 | // You can attribute a maximum of FIVE "tags" for your game. 113 | // Each tag has a specific ID (ex: 22 for the category "Prototype", 101 for the tag "Science-fiction theme game") 114 | // Please see the "Game meta information" entry in the BGA Studio documentation for a full list of available tags: 115 | // http://en.doc.boardgamearena.com/Game_meta-information:_gameinfos.inc.php 116 | // IMPORTANT: this list should be ORDERED, with the most important tag first. 117 | // IMPORTANT: it is mandatory that the FIRST tag is 1, 2, 3 and 4 (= game category) 118 | 'tags' => array( 2 ), 119 | 120 | 121 | //////// BGA SANDBOX ONLY PARAMETERS (DO NOT MODIFY) 122 | 123 | // simple : A plays, B plays, C plays, A plays, B plays, ... 124 | // circuit : A plays and choose the next player C, C plays and choose the next player D, ... 125 | // complex : A+B+C plays and says that the next player is A+B 126 | 'is_sandbox' => false, 127 | 'turnControl' => 'simple' 128 | 129 | //////// 130 | ); 131 | -------------------------------------------------------------------------------- /gameoptions.inc.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 6 | * SharedCode implementation : © Alena Laskavaia 7 | * 8 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 9 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 10 | * ----- 11 | * 12 | * gameoptions.inc.php 13 | * 14 | * SharedCode game options description 15 | * 16 | * In this file, you can define your game options (= game variants). 17 | * 18 | * Note: If your game has no variant, you don't have to modify this file. 19 | * 20 | * Note²: All options defined in this file should have a corresponding "game state labels" 21 | * with the same ID (see "initGameStateLabels" in sharedcode.game.php) 22 | * 23 | * !! It is not a good idea to modify this file when a game is running !! 24 | * 25 | */ 26 | 27 | $game_options = array( 28 | 29 | /* Example of game variant: 30 | 31 | 32 | // note: game variant ID should start at 100 (ie: 100, 101, 102, ...). The maximum is 199. 33 | 100 => array( 34 | 'name' => totranslate('my game option'), 35 | 'values' => array( 36 | 37 | // A simple value for this option: 38 | 1 => array( 'name' => totranslate('option 1') ) 39 | 40 | // A simple value for this option. 41 | // If this value is chosen, the value of "tmdisplay" is displayed in the game lobby 42 | 2 => array( 'name' => totranslate('option 2'), 'tmdisplay' => totranslate('option 2') ), 43 | 44 | // Another value, with other options: 45 | // beta=true => this option is in beta version right now. 46 | // nobeginner=true => this option is not recommended for beginners 47 | 3 => array( 'name' => totranslate('option 3'), 'beta' => true, 'nobeginner' => true ),) ) 48 | ) 49 | ) 50 | 51 | */ 52 | 53 | ); 54 | 55 | 56 | -------------------------------------------------------------------------------- /img/110_110_dice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/110_110_dice.png -------------------------------------------------------------------------------- /img/25_25_wooden_tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/25_25_wooden_tokens.png -------------------------------------------------------------------------------- /img/30_30_colored_dice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/30_30_colored_dice.png -------------------------------------------------------------------------------- /img/30_30_meeple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/30_30_meeple.png -------------------------------------------------------------------------------- /img/30_30_tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/30_30_tokens.png -------------------------------------------------------------------------------- /img/30_30_wooden_cubes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/30_30_wooden_cubes.png -------------------------------------------------------------------------------- /img/64_64_dice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/64_64_dice.jpg -------------------------------------------------------------------------------- /img/78_64_stand_meeples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/78_64_stand_meeples.png -------------------------------------------------------------------------------- /img/castle_iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/castle_iso.png -------------------------------------------------------------------------------- /img/castle_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/castle_top.png -------------------------------------------------------------------------------- /img/cube_iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/cube_iso.png -------------------------------------------------------------------------------- /img/cube_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/cube_top.png -------------------------------------------------------------------------------- /img/cubes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/cubes.png -------------------------------------------------------------------------------- /img/cylinder_iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/cylinder_iso.png -------------------------------------------------------------------------------- /img/cylinder_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/cylinder_top.png -------------------------------------------------------------------------------- /img/game_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/game_box.png -------------------------------------------------------------------------------- /img/game_box180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/game_box180.png -------------------------------------------------------------------------------- /img/game_box75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/game_box75.png -------------------------------------------------------------------------------- /img/game_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/game_icon.png -------------------------------------------------------------------------------- /img/house_iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/house_iso.png -------------------------------------------------------------------------------- /img/house_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/house_top.png -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/logo.png -------------------------------------------------------------------------------- /img/meeple_iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/meeple_iso.png -------------------------------------------------------------------------------- /img/meeple_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/meeple_top.png -------------------------------------------------------------------------------- /img/publisher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/12bcbefce8909f832e907b907fccba892b0b6c45/img/publisher.png -------------------------------------------------------------------------------- /material.inc.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 5 | * SharedCode implementation : © Alena Laskavaia 6 | * 7 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 8 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 9 | * ----- 10 | * 11 | * material.inc.php 12 | * 13 | * SharedCode game material description 14 | * 15 | * Here, you can describe the material of your game with PHP variables. 16 | * 17 | * This file is loaded in your game logic class constructor, ie these variables 18 | * are available everywhere in your game logic code. 19 | * 20 | */ 21 | 22 | $this->token_types = array( 23 | // --- gen php begin --- 24 | // #this used to generate part of matherial.inc.php using genmat.php 25 | 'wcube' => array( 26 | 'type' => 'cube', 27 | 'name' => clienttranslate("Cube"), 28 | ), 29 | 'card_red' => array( 30 | 'type' => 'card', 31 | 'name' => clienttranslate("Red Spell"), 32 | 'tooltip' => clienttranslate("This is tooltip for red spell"), 33 | 't'=>1,'cn'=>'red','ipos'=>3, 34 | ), 35 | 'card_blue' => array( 36 | 'type' => 'card', 37 | 'name' => clienttranslate("Blue Spell"), 38 | 'tooltip' => clienttranslate("This is tooltip for blue spell"), 39 | 'tooltip_action' => clienttranslate("Click to cast it"), 40 | 't'=>2,'cn'=>'blue','ipos'=>4, 41 | ), 42 | 'card_green' => array( 43 | 'type' => 'card', 44 | 'name' => clienttranslate("Green Spell"), 45 | 't'=>3,'cn'=>'green','ipos'=>6, 46 | ), 47 | // --- gen php end --- 48 | ); 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /misc/NOTES.txt: -------------------------------------------------------------------------------- 1 | Seamless background 2 | 3 | https://www.imgonline.com.ua/eng/make-seamless-texture-result.php 4 | 5 | PDF to png convertion (linux) 6 | 7 | gs -sDEVICE=pngalpha -o output.png -r600 -dDownScaleFactor=3 input.pdf 8 | 9 | PSD Extraction (image magic - CMYK to sRBG - one layer per file) 10 | 11 | for i in *.psd; do /usr/bin/convert $i -profile /usr/share/color/icc/colord/sRGB.icc `basename $i .psd`.png; done; 12 | 13 | 14 | PSD extraction (adobe file format) 15 | 16 | https://www.photopea.com/ 17 | 18 | Tiling 19 | 20 | /usr/bin/montage -colorspace sRGB -density 300 *.png -tile 6 -background transparent ../tokens.png 21 | -------------------------------------------------------------------------------- /misc/bgaprojectrename.php: -------------------------------------------------------------------------------- 1 | [options]\n"; 12 | echo "options:\n"; 13 | echo " --all - renamed in text files and strings also"; 14 | echo " --old-name \n"; 15 | echo " --new-name \n"; 16 | exit(0); 17 | } 18 | 19 | $oldprojectpath = realpath($args [0]); 20 | if ($oldprojectpath===false) die("Path does not exists: $args[0]\n"); 21 | $newprojectpath = $args [1]; 22 | if (!is_dir($newprojectpath)) { 23 | mkdir($newprojectpath, 0777, true) || die ("Cannot create dir $newprojectpath\n"); 24 | } 25 | $newprojectpath = realpath($newprojectpath); 26 | if ($newprojectpath===false) die("Path does not exists: $args[1]\n"); 27 | 28 | $oldprojectname = $options['old-name'] ?? basename($oldprojectpath); 29 | $newprojectname = $options['new-name'] ?? basename($newprojectpath); 30 | $replace_content = array_key_exists('all',$options); 31 | echo "replace all: $replace_content\n"; 32 | $res = preg_match( "/^[a-z]+$/", $newprojectname ); 33 | if ($res !== 1) { 34 | echo "Error: new project name can only have [a-z] letters (was $newprojectname)\n"; 35 | exit(1); 36 | } 37 | echo "$oldprojectpath:$oldprojectname => $newprojectpath:$newprojectname\n"; 38 | 39 | $subdirs = ["src","modules",".vscode"]; 40 | 41 | copyr($oldprojectpath, $newprojectpath); 42 | replacecontent($newprojectpath); 43 | 44 | 45 | 46 | 47 | // UTILS 48 | function replacecontent($newprojectpath) { 49 | global $oldprojectname; 50 | global $newprojectname; 51 | global $replace_content; 52 | global $subdirs; 53 | $dir_handle = opendir($newprojectpath); 54 | while ( $file = readdir($dir_handle) ) { 55 | if ($file != "." && $file != "..") { 56 | $path = "$newprojectpath/$file"; 57 | 58 | if (is_dir($path)) { 59 | // only some subdirs 60 | if (array_search($file, $subdirs)!==false) { 61 | replacecontent($path); 62 | } 63 | } else { 64 | // file 65 | $corrfile = $file; 66 | $corrfile = preg_replace( "/\\b${oldprojectname}\\b/", "${newprojectname}", $corrfile); 67 | $corrfile = preg_replace( "/\\b${oldprojectname}_${oldprojectname}\\b/", "${newprojectname}_${newprojectname}", $corrfile); 68 | if ($corrfile != $file) { 69 | echo "Renaming $file => $corrfile\n"; 70 | rename($path,"$newprojectpath/$corrfile"); 71 | $path="$newprojectpath/$corrfile"; 72 | } 73 | echo "Replacing string in $corrfile\n"; 74 | 75 | 76 | $content = file_get_contents( $path ); 77 | if ($corrfile != $file) { 78 | $content = preg_replace( "/\\b${file}\\b/", "${corrfile}", $content); 79 | } 80 | 81 | $content= preg_replace( "/${oldprojectname} implementation/i", "${newprojectname} implementation", $content); 82 | $content= preg_replace( "/\"bgagame\\.${oldprojectname}\"/", "\"bgagame.${newprojectname}\"", $content); 83 | $content= preg_replace( "/${oldprojectname}_${oldprojectname}/", "${newprojectname}_${newprojectname}", $content); 84 | $content= preg_replace( "/action_${oldprojectname}\\b/", "action_${newprojectname}", $content); 85 | $content= preg_replace( "/\\/${oldprojectname}\\/${oldprojectname}\\//", "/${newprojectname}/${newprojectname}/", $content); 86 | $content= preg_replace( "/class ${oldprojectname} extends/i", "class $newprojectname extends", $content); 87 | //function __construct( 88 | $content= preg_replace( "/function\\s+${oldprojectname}\\(/i", "function __construct\\(", $content); 89 | $content= preg_replace("/game_version_${oldprojectname}/", "game_version_${newprojectname}", $content); 90 | $content= preg_replace("/${oldprojectname}\.js/i", "${newprojectname}.js", $content); 91 | $content= preg_replace("/${oldprojectname}\.css/", "${newprojectname}.css", $content); 92 | $content= preg_replace("/${oldprojectname}\.scss/", "${newprojectname}.scss", $content); 93 | $content= preg_replace("/${oldprojectname}\.game.php/", "${newprojectname}.game.php", $content); 94 | $content= preg_replace("/${oldprojectname}\.action.php/", "${newprojectname}.action.php", $content); 95 | $content= preg_replace("/${oldprojectname} game/i", "${newprojectname} game", $content); 96 | $content= preg_replace("/bga.${oldprojectname}/", "bga.${newprojectname}", $content); 97 | $content= preg_replace("/\/${oldprojectname}\//", "/${newprojectname}/", $content); 98 | $content= preg_replace("/return \"${oldprojectname}\"/", "return \"${newprojectname}\"", $content); 99 | 100 | if ($replace_content) { 101 | $content= preg_replace( "/\"${oldprojectname}\"/", "\"${newprojectname}\"", $content); 102 | $content= preg_replace( "/\'${oldprojectname}\'/", "\'${newprojectname}\'", $content); 103 | } 104 | 105 | file_put_contents($path,$content); 106 | } 107 | } 108 | } 109 | closedir($dir_handle); 110 | } 111 | 112 | function startsWith($haystack, $needle) { 113 | // search backwards starting from haystack length characters from the end 114 | return $needle === "" || strrpos($haystack, $needle, - strlen($haystack)) !== false; 115 | } 116 | 117 | function copyr($source, $dest) { 118 | global $oldprojectname; 119 | if (!is_dir($dest)) mkdir($dest); 120 | if (is_dir($source)) { 121 | $dir_handle = opendir($source); 122 | while ( $file = readdir($dir_handle) ) { 123 | if ($file != "." && $file != ".." && $file != ".svn" && $file != ".git" && $file != "node_modules") { 124 | if (is_dir($source . "/" . $file)) { 125 | if (! is_dir($dest . "/" . $file)) { 126 | mkdir($dest . "/" . $file); 127 | } 128 | copyr($source . "/" . $file, $dest . "/" . $file); 129 | } else { 130 | if (endsWith($file, '.game.php')) { 131 | $oldprojectname = preg_replace("/.game.php/", "", $file); 132 | echo "Using $oldprojectname as old project name\n"; 133 | } 134 | copy($source . "/" . $file, $dest . "/" . $file); 135 | } 136 | } 137 | } 138 | closedir($dir_handle); 139 | } else { 140 | copy($source, $dest); 141 | } 142 | } 143 | 144 | function endsWith($haystack, $needle) { 145 | $length = strlen($needle); 146 | return $length === 0 || (substr($haystack, -$length) === $needle); 147 | } 148 | ?> 149 | -------------------------------------------------------------------------------- /misc/bgg_scrabber.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * example: 9 | * bgg_scrabber.php /home/alena/games/khronos 25674 10 | */ 11 | 12 | function varsubquoted($key, $value, $incontents) { 13 | $incontents = preg_replace("/'$key'\s*=>.*/", "'$key' => '$value',", $incontents); 14 | return $incontents; 15 | } 16 | function varsubdirect($key, $value, $incontents) { 17 | $incontents = preg_replace("/'$key'\s*=>.*/", "'$key' => $value,", $incontents); 18 | return $incontents; 19 | } 20 | function totranslate($x) { 21 | return $x; // stub 22 | } 23 | 24 | // MAIN 25 | $indir = $argv [1]; 26 | $infile = "$indir/gameinfos.inc.php"; 27 | 28 | if (isset($argv [2])) { 29 | $bgg_id = $argv [2]; 30 | } else { 31 | require_once $infile; 32 | $bgg_id = $gameinfos['bgg_id']; 33 | } 34 | $outfile = $infile . ".out"; 35 | //$out = fopen($outfile, "w") or die("Unable to open file! $outfile"); 36 | $incontents = file_get_contents($infile) or die("Cannot open $infile"); 37 | $xmldata = file_get_contents("https://www.boardgamegeek.com/xmlapi2/thing?id=$bgg_id") or die("Cannot get bgg content");; 38 | //$xmldata = file_get_contents("bgg_example.xml"); 39 | //print($xmldata); 40 | $xml = simplexml_load_string($xmldata) or die("Error: Cannot create xml object for $bgg_id"); 41 | $item = $xml->item; 42 | //print_r($item); 43 | 44 | 45 | $links = $item->link; 46 | $links_arr = array (); 47 | foreach ( $links as $i => $link ) { 48 | $atts = $link->attributes(); 49 | $type = ( string ) $atts->type; 50 | if ($type) { 51 | if (! array_key_exists($type, $links_arr)) { 52 | $links_arr [$type] = array (); 53 | } 54 | $value = (String)$atts -> value; 55 | $id = (String)$atts -> id; 56 | $links_arr [$type] [$id] = $value; 57 | } 58 | } 59 | print_r($links_arr); 60 | 61 | $name = (String) $item->name [0]->attributes() ['value']; 62 | $incontents = varsubquoted("game_name", $name, $incontents); 63 | $designer = implode(",", $links_arr['boardgamedesigner']); 64 | $incontents = varsubquoted("designer", $designer, $incontents); 65 | $incontents = varsubquoted("artist", implode(",", $links_arr['boardgameartist']), $incontents); 66 | $pubs =$links_arr['boardgamepublisher']; 67 | foreach($pubs as $id => $name) { 68 | $incontents = varsubquoted("publisher", $name, $incontents); 69 | $incontents = varsubdirect("publisher_bgg_id", $id, $incontents); 70 | $publisher_id = $id; 71 | break; 72 | } 73 | $incontents = varsubdirect("bgg_id", $bgg_id, $incontents); 74 | $minplayers = (String) $item->minplayers->attributes() ['value']; 75 | $maxplayers = (String) $item->maxplayers->attributes() ['value']; 76 | $plarr = array(); 77 | for ($i=(int)$minplayers;$i<=$maxplayers;$i++) { 78 | $plarr[]=$i; 79 | } 80 | $year = (String) $item->yearpublished->attributes() ['value']; 81 | $incontents = varsubdirect("year", $year, $incontents); 82 | $incontents = varsubdirect("players", "array(". (implode(",",$plarr)).")", $incontents); 83 | $incontents = varsubdirect("estimated_duration", (String) $item->minplaytime->attributes() ['value'], $incontents); 84 | 85 | file_put_contents($outfile, $incontents) or die; 86 | rename($outfile, $infile); 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /misc/game_util.php: -------------------------------------------------------------------------------- 1 | $infile\n"; 42 | } 43 | 44 | function inject_functions($infile, $key, $func_arr) { 45 | $outfile = $infile . ".out"; 46 | $out = fopen($outfile, "w") or die("Unable to open file! $outfile"); 47 | $in = fopen($infile, "r") or die("Unable to open file! $in"); 48 | $genon = false; 49 | while ( ($line = fgets($in)) !== false ) { 50 | fwrite($out, $line); 51 | if (isKeyBegin($line, $key)) { 52 | $genon = true; 53 | break; 54 | } 55 | } 56 | if ($genon) { 57 | while ( ($line = fgets($in)) !== false ) { 58 | if (isKeyEnd($line, $key)) { 59 | foreach ( $func_arr as $name => $text ) { 60 | echo "Generated func => $infile '$name'\n"; 61 | fwrite($out, $text . "\n"); 62 | } 63 | fwrite($out, $line); 64 | $genon = false; 65 | break; 66 | } 67 | if (strpos($line, "function ") !== false) { 68 | $name = trim($line); 69 | $name = preg_replace("/^ *function (.*) *\(.*$/", "\\1", $name); 70 | unset($func_arr [$name]); 71 | echo "-> skip '$name'\n"; 72 | } 73 | fwrite($out, $line); 74 | } 75 | while ( ($line = fgets($in)) !== false ) { 76 | fwrite($out, $line); 77 | } 78 | } 79 | fclose($out); 80 | fclose($in); 81 | rename($outfile, $infile); 82 | } 83 | 84 | function filevarsub($infile, $outfile, $keymap, $page) { 85 | $lines = file($infile, FILE_IGNORE_NEW_LINES); 86 | if ($lines === false) 87 | die("Unable to open file! $infile"); 88 | $outlines = array (); 89 | $stackstates = array (); 90 | $line_num = 0; 91 | $total_lines = count($lines); 92 | while ( $line_num < $total_lines ) { 93 | $line = $lines [$line_num]; 94 | if (preg_match("/-- BEGIN (?P\w+) --/", $line, $matches)) { 95 | $block = $matches ['name']; 96 | if (isset($page->blocks [$block])) { 97 | $args = $page->blocks [$block]; 98 | 99 | } else { 100 | echo "$infile:$line_num:Warning: No rule defined for block $block\n"; 101 | $args = array ( 102 | array () 103 | ); 104 | } 105 | $state = array ( 106 | 'block' => $block, 107 | 'index' => 0, 108 | 'prevmap' => $keymap, 109 | 'linenum' => $line_num, 110 | 'args' => $args 111 | ); 112 | $curmap = array_shift($state ['args']); 113 | $stackstates [] = $state; 114 | $keymap = array_merge($keymap, $curmap); 115 | } 116 | if (preg_match("/-- END (?P\w+) --/", $line, $matches)) { 117 | $block = $matches ['name']; 118 | $state = array_pop($stackstates); 119 | $keymap = $state ['prevmap']; 120 | $curmap = array_shift($state ['args']); 121 | // print_r($curmap); 122 | if ($curmap) { 123 | $keymap = array_merge($keymap, $curmap); 124 | $line_num = $state ['linenum']; 125 | $stackstates [] = $state; 126 | } 127 | } 128 | $outline = varsub($line, $keymap); 129 | $outlines [] = $outline; 130 | $line_num ++; 131 | } 132 | file_put_contents($outfile, implode(PHP_EOL, $outlines)) or die("Unable to open file! $outfile"); 133 | echo "Generated => $outfile\n"; 134 | } 135 | 136 | function varsub($line, $keymap) { 137 | if (strpos($line, "{") !== false) { 138 | foreach ( $keymap as $key => $value ) { 139 | if (strpos($line, "{$key}") !== false) { 140 | $line = preg_replace("/\{$key\}/", $value, $line); 141 | } 142 | } 143 | } 144 | return $line; 145 | } 146 | 147 | function startsWith($haystack, $needle) { 148 | // search backwards starting from haystack length characters from the end 149 | return $needle === "" || strrpos($haystack, $needle, - strlen($haystack)) !== false; 150 | } 151 | 152 | function endsWith($haystack, $needle) { 153 | // search backwards starting from haystack length characters from the end 154 | return $needle === "" || strrpos($haystack, $needle, - strlen($needle)) !== false; 155 | } 156 | -------------------------------------------------------------------------------- /misc/gen_sprite.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /misc/generate_state_diagram.php: -------------------------------------------------------------------------------- 1 | $state) { 13 | $color = "red"; 14 | $shape = "ellipse"; 15 | if ($state["name"] == "gameSetup") { 16 | $shape = "Msquare"; 17 | } 18 | if ($state["name"] == "gameEnd") { 19 | $shape = "Msquare"; 20 | } 21 | if ($state["type"] == "game") { 22 | $color = "orange"; 23 | $shape = "diamond"; 24 | } 25 | if ($state["type"] == "activeplayer") { 26 | $color = "blue"; 27 | } 28 | if ($state["type"] == "multipleactiveplayer") { 29 | $color = "green"; 30 | } 31 | echo "n" . $state_id . " [label=\"".$state_id."_".$state["name"]."\" color=".$color." shape=".$shape."];\n"; 32 | } 33 | 34 | 35 | foreach ($machinestates as $state_id => $state) { 36 | if (array_key_exists("transitions", $state)) { 37 | foreach ($state["transitions"] as $transition_label => $transition) { 38 | echo "n" . $state_id . " -> " . "n". $transition . " [label=\"".$transition_label."\"];\n"; 39 | } 40 | } 41 | } 42 | 43 | echo "}\n"; 44 | ?> 45 | -------------------------------------------------------------------------------- /misc/genmat.php: -------------------------------------------------------------------------------- 1 | ] 8 | * 9 | * Sample input file 10 | * 11 | id|type|name|tooltip|tooltip_action|php 12 | train|token|Locomotive|The number of a locomotive indicates the number of spaces it can reach on a railroad: 13 | factory|token|Factory| 14 | slot_action_1|slot_action|2 Black Track Advancements|This action gives you two advancements of black track. You cannot use this action if you cannot complete all advancements.|'o'=>"1,0,1,bb" 15 | * 16 | * Sample material.inc.php BEFORE change 17 | 18 | $this->token_types = array( 19 | // --- gen php begin --- 20 | 21 | // --- gen php end --- 22 | ); 23 | 24 | * After running script 25 | 26 | $this->token_types = array( 27 | // --- gen php begin --- 28 | 'train' => array( 29 | 'type' => 'token', 30 | 'name' => clienttranslate("Locomotive"), 31 | 'tooltip' => clienttranslate("The number of a locomotive indicates the number of spaces it can reach on a railroad:"), 32 | ), 33 | 'factory' => array( 34 | 'type' => 'token', 35 | 'name' => clienttranslate("Factory"), 36 | ), 37 | 'slot_action_1' => array( 38 | 'type' => 'slot_action', 39 | 'name' => clienttranslate("2 Black Track Advancements"), 40 | 'tooltip' => clienttranslate("This action gives you two advancements of black track. You cannot use this action if you cannot complete all advancements."), 41 | 'o'=>"1,0,1,bb", 42 | ), 43 | // --- gen php end --- 44 | ); 45 | 46 | * If file is not called material.csv, if called foo.csv use // --- gen php begin foo --- 47 | * This way can generate multiple sections 48 | 49 | Special columns: 50 | id - the id of object (string or number) 51 | variant - special field appended to string as as @xxx - used to have versions of material file depending on game variants 52 | -con - way to use php constant to alias an numeric id, defines are currently not injected but dumped on console 53 | name, tooltip, tooltip_action - default fields which are translatable 54 | 55 | #set tr=color - using this directive make 'color' translatable field also 56 | #set sep=, - change separator 57 | #set sub=/ - use this character or string to replace default separator, i.e. a/b will be replaced to a|b 58 | #set noquotes=field - do not quotes for field when outputing 59 | */ 60 | $g_field_names = null; 61 | $g_field_extra = [ ]; 62 | $g_index = 1; 63 | $g_trans = [ 'name','tooltip','tooltip_action' ]; 64 | $g_separator = '|'; 65 | $g_separator_sub = ''; 66 | $g_noquotes = [ ]; 67 | 68 | function handle_header($fields) { 69 | global $g_field_names; 70 | global $g_field_extra; 71 | if ($fields [0] == 'element_id') { 72 | // old style header 73 | $g_field_names = [ 'id','type','name','tooltip','tooltip_action' ]; 74 | } else { 75 | $g_field_names = $fields; 76 | } 77 | return true; 78 | } 79 | 80 | function get_field($key, $fields, $default = null) { 81 | global $g_field_extra; 82 | if ( !is_array($fields)) 83 | throw new Exception("fields should be an array"); 84 | return $fields [$key] ?? $g_field_extra [$key] ?? $default; 85 | } 86 | 87 | function isTranslatable($key) { 88 | global $g_trans; 89 | if (array_search($key, $g_trans) !== false) { 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | function genbody($incsv) { 96 | global $out; 97 | $ins = fopen($incsv, "r") or die("Unable to open file! $ins"); 98 | $comment = ""; 99 | global $g_field_names, $g_index, $g_separator, $g_separator_sub, $g_trans, $g_noquotes; 100 | global $g_field_extra; 101 | $limit = false; 102 | $matches = [ ]; 103 | while ( ($line = fgets($ins)) !== false ) { 104 | $line = trim($line); 105 | if (empty($line)) 106 | continue; 107 | if (startsWith($line, '#')) { 108 | //special comment 109 | if (preg_match("/#set *(?P\w+)=(?P.*)/", $line, $matches)) { 110 | $key = $matches ['name']; 111 | $value = trim($matches ['value']); 112 | switch ($key) { 113 | case '_sep' : 114 | $g_separator = $value; #field separator 115 | break; 116 | case '_sub' : 117 | // use another character insped of sepator if separttor is needed, 118 | // i.e. if sepator is | and you need this in string, you can define / as replacement so / will be replace to | in the string 119 | $g_separator_sub = $value; 120 | break; 121 | case '_tr' : 122 | // field with this name will be translated 123 | $g_trans [] = $value; 124 | break; 125 | case '_noquotes' : 126 | // field with this name will be generated without quotes (i.e. direct constant or php expresson) 127 | $g_noquotes [] = $value; 128 | break; 129 | default : 130 | if (startsWith($key, "_")) { 131 | print("Error: unknown key $key for #set directive\n"); 132 | exit(2); 133 | } else { 134 | $g_field_extra [$key] = $value; 135 | } 136 | } 137 | continue; 138 | } 139 | $comment .= "// $line\n"; 140 | continue; 141 | } 142 | if ($g_field_names == null) { 143 | $raw_fields = explode($g_separator, $line); 144 | $limit = count($raw_fields); 145 | if (handle_header($raw_fields)) 146 | continue; 147 | } 148 | $raw_fields = explode($g_separator, $line, $limit); 149 | $fields = [ ]; 150 | $f = 0; 151 | foreach ( $g_field_names as $key ) { 152 | if (count($raw_fields) >= $f + 1) 153 | $fields [$key] = $raw_fields [$f]; 154 | else 155 | $fields [$key] = null; 156 | $f++; 157 | } 158 | $id = get_field('id', $fields); 159 | if (!$id) { 160 | echo "Error: missing required id column in input file\n"; 161 | exit(2); 162 | } 163 | $ftype = get_field('type', $fields, ''); 164 | $ftype = varsub($ftype, $fields); 165 | $extra = ''; 166 | // old way of getting extra fields 167 | $pos = strpos($ftype, '{'); 168 | if ($pos !== false) { 169 | $extra = substr($ftype, $pos + 1); 170 | $ftype = trim(substr($ftype, 0, $pos)); 171 | $fields ['type'] = $ftype; 172 | $pos = strrpos($extra, '}'); 173 | if ($pos !== false) { 174 | $extra = substr($extra, 0, $pos); 175 | $fields ['php'] = $extra; 176 | } 177 | } 178 | if ($comment) { 179 | fwrite($out, "$comment"); 180 | $comment = ""; 181 | } 182 | $id = varsub($id, $fields); 183 | $fullid = $id; 184 | $variant = get_field('variant', $fields); 185 | if ($variant) 186 | $fullid = "${id}@" . $fields ['variant']; 187 | $con = get_field('-con', $fields); 188 | $concomment = " //"; 189 | if ($con) { 190 | print("define(\"$con\", $id);\n"); 191 | $concomment = " // $con"; 192 | } 193 | if (is_numeric($fullid) || array_search('id', $g_noquotes) !== false) 194 | fwrite($out, " $fullid"); 195 | else 196 | fwrite($out, " '$fullid'"); 197 | fwrite($out, " => [ ${concomment}\n"); 198 | $map = array_merge($g_field_extra, $fields); 199 | foreach ( $fields as $key => $value ) { 200 | if ($value === null && array_key_exists($key, $g_field_extra)) { 201 | $map [$key] = $g_field_extra [$key]; 202 | } 203 | } 204 | 205 | foreach ( $map as $key => $value ) { 206 | if (startsWith($key, "-")) 207 | continue; 208 | if ($key == 'id') 209 | continue; 210 | if ($key == 'variant') 211 | continue; 212 | if ( !$value && $value !== '0') 213 | continue; 214 | $value = str_replace("’", "'", $value); 215 | $value = str_replace("‘", "'", $value); 216 | $value = varsub($value, $fields); 217 | if ($g_separator_sub) 218 | $value = str_replace($g_separator_sub, $g_separator, $value); 219 | if ($key == 'php') { 220 | fwrite($out, " $value,\n"); 221 | continue; 222 | } 223 | if (isTranslatable($key)) { 224 | $value = trim($value); 225 | if ($value) 226 | $exp = "clienttranslate(\"$value\")"; 227 | else 228 | continue; 229 | } else if (is_numeric($value) || array_search($key, $g_noquotes) !== false) { 230 | $exp = $value; 231 | } else { 232 | $exp = "'$value'"; 233 | } 234 | fwrite($out, " '$key' => $exp,\n"); 235 | } 236 | fwrite($out, "],\n"); 237 | } 238 | } 239 | 240 | function startsWith($haystack, $needle) { 241 | // search backwards starting from haystack length characters from the end 242 | return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false; 243 | } 244 | 245 | function endsWith($haystack, $needle) { 246 | // search backwards starting from haystack length characters from the end 247 | return $needle === "" || strrpos($haystack, $needle, -strlen($needle)) !== false; 248 | } 249 | 250 | /** 251 | * subsitute variables like {VARIABLE} using keymap 252 | */ 253 | function varsub($line, $keymap) { 254 | if ($line === null) 255 | throw new Exception("varsub: line cannot be null"); 256 | global $g_index; 257 | global $g_field_extra; 258 | $line = preg_replace("/\{GINDEX\}/", $g_index, $line); 259 | if (strpos($line, "{") !== false) { 260 | $map = array_merge($g_field_extra, $keymap); 261 | foreach ( $map as $key => $value ) { 262 | if (strpos($line, "{$key}") !== false) { 263 | $line = preg_replace("/\{$key\}/", $value, $line); 264 | } 265 | } 266 | } 267 | return $line; 268 | } 269 | // MAIN 270 | $incsv = $argv [1]; 271 | $basename = basename($incsv, '.csv'); 272 | if ($basename == 'material') 273 | $module = ''; 274 | else 275 | $module = "$basename "; 276 | echo "Reading $incsv\n"; 277 | if (isset($argv [2])) { 278 | $infile = $argv [2]; 279 | } else { 280 | $infile = dirname("$incsv") . "/../material.inc.php"; 281 | } 282 | echo "Writing $infile\n"; 283 | $in = fopen($infile, "r") or die("Unable to open file! $in"); 284 | $markup_begin = "--- gen php begin $module---"; 285 | $markup_end = "--- gen php end $module---"; 286 | $outfile = $infile . ".out"; 287 | $out = fopen($outfile, "w") or die("Unable to open file! $outfile"); 288 | //$out = fopen("php://stdout", "w") or die("Unable to open file! $outfile"); 289 | // find markup line 290 | $found = false; 291 | while ( ($line = fgets($in)) !== false ) { 292 | fwrite($out, $line); 293 | $line = trim($line); 294 | $k = strpos($line, $markup_begin); 295 | if ($k !== false) { 296 | $found = true; 297 | break; 298 | } 299 | } 300 | if ( !$found) { 301 | echo "Error: missing markup $markup_begin in $infile\n"; 302 | unlink($outfile); 303 | exit(2); 304 | } 305 | // generate data 306 | genbody($incsv); 307 | // skip all lines until end markup 308 | while ( ($line = fgets($in)) !== false ) { 309 | if (strpos($line, $markup_end) !== false) { 310 | fwrite($out, $line); 311 | break; 312 | } 313 | } 314 | // write rest of file 315 | while ( ($line = fgets($in)) !== false ) { 316 | fwrite($out, $line); 317 | } 318 | fclose($out); 319 | fclose($in); 320 | rename($outfile, $infile); 321 | echo "Generated => $infile\n"; 322 | ?> -------------------------------------------------------------------------------- /misc/genstate.php: -------------------------------------------------------------------------------- 1 | \n"; 7 | exit(0); 8 | } 9 | 10 | require_once 'game_util.php'; 11 | 12 | $indir = $argv [1]; 13 | $gamename = basename($indir); 14 | 15 | 16 | /** 17 | * This script reads ${gamename}.states.php and generates states.inc.php, ${gamename}.action.php, 18 | * and then injects actions, game state functions and state args functions into ${gamename}.game.php, 19 | * 20 | * Usage: genstate.php 21 | */ 22 | require_once "$indir/${gamename}.states.php"; 23 | 24 | function clienttranslate($str) { 25 | return $str; 26 | } 27 | 28 | require_once "$indir/states.inc.php"; 29 | $prev_machinestates = $machinestates; 30 | $prev_gamestate = array (); 31 | foreach ( $prev_machinestates as $state_num => $stateinfo ) { 32 | $statename = $stateinfo ['name']; 33 | $prev_gamestate [$statename] = $stateinfo; 34 | $prev_gamestate [$statename] ['number'] = $state_num; 35 | } 36 | $actions = array (); 37 | $game_func_arr = array (); 38 | $player_func_arr = array (); 39 | $state_args_arr = array (); 40 | $machinestates = array ( 41 | 1 => array ( 42 | "name" => "gameSetup", 43 | "description" => "Game setup", 44 | "type" => "manager", 45 | "action" => "stGameSetup", 46 | "transitions" => array ( 47 | "" => 2 48 | ) 49 | ), 50 | 99 => array ( 51 | "name" => "gameEnd", 52 | "description" => "End of game", 53 | "type" => "manager", 54 | "action" => "stGameEnd", 55 | "args" => "argGameEnd" 56 | ) 57 | ); 58 | $stateNumber = 2; 59 | 60 | foreach ( $gamestates as $statename => $stateinfo ) { 61 | if (isset($prev_gamestate [$statename] ['number'])) { 62 | $stateNumber = $prev_gamestate [$statename] ['number']; 63 | } else { 64 | while ( isset($prev_machinestates [$stateNumber]) ) { 65 | $stateNumber ++; 66 | } 67 | } 68 | $gamestates [$statename] ['number'] = $stateNumber; 69 | echo "$statename => $stateNumber\n"; 70 | if (isset($stateinfo['startingState'])) { 71 | if ($stateinfo['startingState']) { 72 | $machinestates[1]["transitions"] = array ( 73 | "" => $stateNumber 74 | ); 75 | } 76 | } 77 | $stateNumber ++; 78 | } 79 | foreach ( $gamestates as $statename => $stateinfo ) { 80 | if (! $stateinfo ['type']) 81 | die("Missing type $statename"); 82 | $stateNumber = $gamestates [$statename] ['number']; 83 | $type = $stateinfo ['type']; 84 | switch ($type) { 85 | case 'activeplayer' : 86 | case 'multiplayer' : 87 | case 'multipleactiveplayer': 88 | $player = true; 89 | break; 90 | case 'game' : 91 | $player = false; 92 | break; 93 | default : 94 | die("Unknown type $type for $statename"); 95 | } 96 | $trans_full = $stateinfo ['transitions']; 97 | $gamestates [$statename] ['transitions'] = array (); 98 | $firsttrans = ''; 99 | foreach ( $trans_full as $trans_name => $trans_state ) { 100 | if ($trans_name == 'loopback') 101 | $trans_state = $statename; 102 | if ($trans_state == 'endGame') 103 | $num = 99; 104 | else 105 | $num = $gamestates [$trans_state] ['number']; 106 | $gamestates [$statename] ['transitions'] [$trans_name] = $num; 107 | if (! $firsttrans) 108 | $firsttrans = $trans_name; 109 | } 110 | if ($player) { 111 | $actions_full = $stateinfo ['possibleactions']; 112 | $gamestates [$statename] ['possibleactions'] = array (); 113 | foreach ( $actions_full as $action_str ) { 114 | $k = strpos($action_str, '('); 115 | $args = array (); 116 | if ($k !== false) { 117 | $action_name = substr($action_str, 0, $k); 118 | $l = strpos($action_str, ')'); 119 | if ($l === false) 120 | die("Missing ) in $action_name"); 121 | $args_str = substr($action_str, $k + 1, $l - $k - 1); 122 | $args_arr = explode(',', $args_str); 123 | 124 | foreach ( $args_arr as $arg_str ) { 125 | if (!$arg_str) continue; 126 | list ( $atype, $avar ) = explode(' ', trim($arg_str)); 127 | if (endsWith($avar,'?')) { 128 | $avar = substr($avar, 0, -1); 129 | $args [$avar] = "\$$avar = self::getArg('$avar', AT_$atype, false, null);"; 130 | } else { 131 | $args [$avar] = "\$$avar = self::getArg('$avar', AT_$atype, true);"; 132 | } 133 | } 134 | } else { 135 | $action_name = $action_str; 136 | } 137 | $actions [$action_name] = $args; 138 | $gamestates [$statename] ['possibleactions'] [] = $action_name; 139 | } 140 | if (! isset($stateinfo ['args'])) { 141 | $gamestates [$statename] ['args'] = "arg_$statename"; 142 | } 143 | } else { 144 | if (! isset($stateinfo ['action'])) { 145 | $gamestates [$statename] ['action'] = "st_$statename"; 146 | } 147 | } 148 | if (isset($gamestates [$statename] ['action'])) { 149 | $stfunc = $gamestates [$statename] ['action']; 150 | $game_func_arr [$stfunc] = " function $stfunc() {\n \$this->gamestate->nextState( '$firsttrans' );\n }"; 151 | } 152 | if (isset($gamestates [$statename] ['args'])) { 153 | $argfunc = $gamestates [$statename] ['args']; 154 | $state_args_arr [$argfunc] = " function $argfunc() {\n return array();\n }"; 155 | } 156 | $machinestates [$stateNumber] = $gamestates [$statename]; 157 | $machinestates [$stateNumber] ['name'] = $statename; 158 | unset($machinestates [$stateNumber] ['number']); 159 | } 160 | $states_str = var_export($machinestates, true) . ";"; 161 | $states_str = preg_replace("/^ *'description' => ('.*'),$/m", " 'description' => clienttranslate(\\1),", $states_str); 162 | $states_str = preg_replace("/^ *'descriptionmyturn' => ('.*'),$/m", " 'descriptionmyturn' => clienttranslate(\\1),", $states_str); 163 | inject("$indir/states.inc.php", 'generated states', $states_str); 164 | $infile = "$indir/${gamename}.action.php"; 165 | $body = ""; 166 | foreach ( $actions as $action => $args ) { 167 | $func = " 168 | public function $action() { 169 | self::setAjaxMode();\n"; 170 | $vars = array (); 171 | foreach ( $args as $aname => $acall ) { 172 | $vars [] = "\$$aname"; 173 | $func .= " $acall\n"; 174 | } 175 | $call = implode(', ', $vars); 176 | $func .= " \$this->game->action_$action( $call ); 177 | self::ajaxResponse( ); 178 | }"; 179 | $body .= $func . "\n"; 180 | // action funcs 181 | $checkArgs = ""; 182 | foreach ( $args as $aname => $acall) { 183 | $var = "\$$aname"; 184 | $checkArgs .= " \$this->check_$aname( $var );\n"; 185 | } 186 | $player_func_arr ["action_$action"] = " 187 | function action_$action($call) { 188 | \$this->checkAction( '$action' ); 189 | $checkArgs 190 | \$player_id = \$this->getActivePlayerId(); 191 | \$this->notifyWithName( '$action', clienttranslate( '\${player_name} $action' ), array()); 192 | \$this->gamestate->nextState( 'next' ); 193 | }"; 194 | } 195 | inject($infile, 'generated actions', $body); 196 | inject_functions("$indir/${gamename}.game.php", 'Game state actions generated', $game_func_arr); 197 | inject_functions("$indir/${gamename}.game.php", 'Player actions generated', $player_func_arr); 198 | inject_functions("$indir/${gamename}.game.php", 'Game state arguments generated', $state_args_arr); 199 | ?> 200 | -------------------------------------------------------------------------------- /misc/mat_test.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /misc/material.csv: -------------------------------------------------------------------------------- 1 | element_id|element_type|element_name|element_description|action 2 | #this used to generate part of matherial.inc.php using genmat.php 3 | wcube|cube|Cube 4 | card_red|card {'t'=>1,'cn'=>'red','ipos'=>3}|Red Spell|This is tooltip for red spell 5 | card_blue|card {'t'=>2,'cn'=>'blue','ipos'=>4}|Blue Spell|This is tooltip for blue spell|Click to cast it 6 | card_green|card {'t'=>3,'cn'=>'green','ipos'=>6}|Green Spell -------------------------------------------------------------------------------- /misc/measure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 29 | 30 | 31 | 32 | Use this tool for image markup 33 | 34 |
35 | Sprite Div 37 | Sprite Div Per 40 | Map Coords 42 | Map Coords Calc 45 |
46 | Selected: 47 |
48 | Width: 49 | Height:
50 | Name:
51 | Counter: 52 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 |
67 |
68 |
69 | 70 | 71 | If you using sprite this is how div will look like: 72 |
73 | Resulting code: 74 |
75 | 76 | 256 | 257 | -------------------------------------------------------------------------------- /misc/module/common/deck.game.php: -------------------------------------------------------------------------------- 1 | object, 'method' => method_name ) 26 | // If defined, tell the name of the deck and what is the corresponding discard (ex : "mydeck" => "mydiscard") 27 | var $autoreshuffle_custom = array (); 28 | 29 | 30 | function __construct() { 31 | $this->table = 'card'; 32 | } 33 | 34 | // MUST be called before any other method if db table is not called 'card' 35 | function init($table) { 36 | $this->table = $table; 37 | } 38 | 39 | // This is the way cards are created and should not be called during the game. 40 | // Cards are added to the deck (not shuffled) 41 | // Cards is an array of "card types" with at least the followin fields: 42 | // array( 43 | // array( // This is my first card type 44 | // "type" => "name of this type" // Note: <10 caracters 45 | // "type_arg" => // Argument that should be applied to all card of this card type 46 | // "nbr" => // Number of cards with this card type to create in game 47 | // 48 | // If location_arg is not specified, cards are placed at location extreme position 49 | function createCards($cards, $location_global, $card_state_global = null) { 50 | return; 51 | } 52 | 53 | // Get max on min state on the specific location 54 | function getExtremePosition($getMax, $location, $card_key = null) { 55 | return 0; 56 | } 57 | 58 | // Shuffle card of a specified location, result of the operation will changes state of the card to be a position after shuffling 59 | function shuffle($location) { 60 | 61 | } 62 | 63 | function deleteAll() { 64 | self::DbQuery("DELETE FROM " . $this->table); 65 | } 66 | 67 | // Pick the first card on top of specified deck and give it to specified player 68 | // Return card infos or null if no card in the specified location 69 | function pickCard( $location, $player_id ) 70 | { 71 | return self::pickCardForLocation( $location, "hand", $player_id ); 72 | } 73 | 74 | // Pick the "nbr" first cards on top of specified deck and give it to specified player 75 | // Return card infos (array) or null if no card in the specified location 76 | function pickCards( $nbr, $location, $player_id ) 77 | { 78 | return self::pickCardsForLocation( $nbr, $location, "hand", $player_id ); 79 | } 80 | 81 | // Pick the first card on top of specified deck and place it in target location 82 | // Return card infos or null if no card in the specified location 83 | function pickCardForLocation( $from_location, $to_location, $location_arg=0 ) 84 | { 85 | return null; 86 | } 87 | 88 | // Pick the first "$nbr" cards on top of specified deck and place it in target location 89 | // Return cards infos or void array if no card in the specified location 90 | function pickCardsForLocation($nbr, $from_location, $to_location, $location_arg = 0, $no_deck_reform = false) { 91 | self::checkLocation($from_location); 92 | return []; 93 | } 94 | 95 | 96 | 97 | /** 98 | * Return card on top of this location, top defined as item with higher state value 99 | */ 100 | function getCardOnTop($location) { 101 | self::checkLocation($location); 102 | return null; 103 | } 104 | 105 | /** 106 | * Return "$nbr" cards on top of this location, top defined as item with higher state value 107 | */ 108 | function getCardsOnTop($nbr, $location) { 109 | self::checkLocation($location); 110 | return []; 111 | } 112 | 113 | function reformDeckFromDiscard( $from_location='deck' ) { 114 | self::checkLocation($from_location); 115 | } 116 | 117 | 118 | // Move a card to specific location 119 | function moveCard( $card_id, $location, $location_arg=0 ) 120 | { 121 | self::checkLocation($location); 122 | self::checkLocationArg($location_arg); 123 | } 124 | 125 | // Move cards to specific location 126 | function moveCards( $cards, $location, $location_arg=0 ) 127 | { 128 | self::checkLocation($location); 129 | self::checkLocationArg($location_arg); 130 | } 131 | 132 | // Move a card to a specific location where card are ordered. If location_arg place is already taken, increment 133 | // all cards after location_arg in order to insert new card at this precise location 134 | function insertCard( $card_id, $location, $location_arg ) { 135 | self::checkLocation($location); 136 | self::checkLocationArg($location_arg); 137 | 138 | } 139 | 140 | function insertCardOnExtremePosition($card_key, $location, $bOnTop) { 141 | $extreme_pos = self::getExtremePosition($bOnTop, $location); 142 | if ($bOnTop) 143 | self::insertCard($card_key, $location, $extreme_pos + 1); 144 | else 145 | self::insertCard($card_key, $location, $extreme_pos - 1); 146 | } 147 | 148 | // Move all cards from a location to another 149 | // !!! state is reset to 0 or specified value !!! 150 | // if "from_location" and "from_state" are null: move ALL cards to specific location 151 | function moveAllCardsInLocation($from_location, $to_location, $from_location_arg = null, $to_location_arg = 0) { 152 | if ($from_location != null) 153 | self::checkLocation($from_location); 154 | self::checkLocation($to_location); 155 | } 156 | 157 | /** 158 | * Move all cards from a location to another location arg stays with the same value 159 | */ 160 | function moveAllCardsInLocationKeepOrder($from_location, $to_location) { 161 | self::checkLocation($from_location); 162 | self::checkLocation($to_location); 163 | 164 | } 165 | 166 | // Return all cards in specific location 167 | // note: if "order by" is used, result object is NOT indexed by card ids 168 | function getCardsInLocation( $location, $location_arg = null, $order_by = null ) 169 | { 170 | return []; 171 | } 172 | 173 | function getPlayerHand( $player_id ) 174 | { 175 | return self::getCardsInLocation( "hand", $player_id ); 176 | } 177 | 178 | 179 | // Get specific card infos 180 | function getCard( $card_id ) 181 | { 182 | $sql = "SELECT card_id id, card_type type, card_type_arg type_arg, card_location location, card_location_arg location_arg "; 183 | $sql .= "FROM ".$this->table; 184 | $sql .= " WHERE card_id='$card_id' "; 185 | $dbres = self::DbQuery( $sql ); 186 | return mysql_fetch_assoc( $dbres ); 187 | } 188 | 189 | // Get specific cards infos 190 | function getCards( $cards_array ) 191 | { 192 | return []; 193 | } 194 | 195 | 196 | // Get cards from their IDs (same as getCards), but with a location specified. Raises an exception if the cards are not in the specified location. 197 | function getCardsFromLocation( $cards_array, $location, $location_arg = null ) 198 | { 199 | return []; 200 | } 201 | 202 | // Get card of a specific type 203 | function getCardsOfType( $type, $type_arg=null ) 204 | { 205 | return []; 206 | } 207 | 208 | // Get cards of a specific type in a specific location 209 | function getCardsOfTypeInLocation( $type, $type_arg=null, $location=null, $location_arg = null ) 210 | { 211 | return []; 212 | } 213 | 214 | // Move a card to discard pile 215 | function playCard( $card_id ) 216 | { 217 | 218 | } 219 | 220 | 221 | // Return count of cards in location with optional arg 222 | function countCardsInLocation( $location, $location_arg=null ) 223 | { 224 | return 0; 225 | } 226 | 227 | // Return an array "location" => number of cards 228 | function countCardsInLocations( ) 229 | { 230 | 231 | return []; 232 | } 233 | // Return an array "location_arg" => number of cards (for this location) 234 | function countCardsByLocationArgs( $location ) 235 | { 236 | return []; 237 | 238 | } 239 | 240 | final function checkLocation($location, $like = false) { 241 | if ($location == null) 242 | throw new feException("location cannot be null"); 243 | $extra = ""; 244 | if ($like) 245 | $extra = "%"; 246 | if (preg_match("/^[A-Za-z{$extra}][A-Za-z_0-9{$extra}-]*$/", $location) == 0) { 247 | throw new feException("location must be alphanum and underscore non empty string"); 248 | } 249 | } 250 | 251 | final function checkLocationArg($location_arg, $canBeNull = false) { 252 | if ($location_arg === null && $canBeNull == false) 253 | throw new feException("state cannot be null"); 254 | if ($location_arg !== null && preg_match("/^-*[0-9]+$/", $location_arg) == 0) { 255 | // $bt = debug_backtrace(); 256 | // trigger_error("bt ".print_r($bt[2],true)) ; 257 | throw new feException("state must be integer number"); 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /misc/parser_trans.php: -------------------------------------------------------------------------------- 1 | keys_to_translate = array (); 12 | } 13 | 14 | /** 15 | * Original parsing function 16 | */ 17 | function parse_php($file) { 18 | $content = file_get_contents( $file ); 19 | $nbr = 0; 20 | // Remove php comments 21 | $content = preg_replace( "/^ *\/\/.*?$/m", "", $content ); 22 | $content = preg_replace( "/\/\*.*?\*\//s", "", $content ); 23 | // Serverside 24 | // _( "key" ) 25 | preg_match_all( "/_\( *(\"(.*?)\") *\)/s", $content, $matches ); 26 | foreach ( $matches [2] as $val ) { 27 | self::addKey( stripcslashes( $val ), false, true ); 28 | $nbr ++; 29 | } 30 | // _( 'key' ) 31 | preg_match_all( "/_\( *('(.*?)') *\)/s", $content, $matches ); 32 | foreach ( $matches [2] as $val ) { 33 | self::addKey( stripcslashes( $val ), false, true ); 34 | $nbr ++; 35 | } 36 | // totranslate( "key" ) 37 | preg_match_all( "/totranslate\( *(\"(.*?)\") *\)/s", $content, $matches ); 38 | foreach ( $matches [2] as $val ) { 39 | self::addKey( stripcslashes( $val ), false, true ); 40 | $nbr ++; 41 | } 42 | // totranslate( 'key' ) 43 | preg_match_all( "/totranslate\( *('(.*?)') *\)/s", $content, $matches ); 44 | foreach ( $matches [2] as $val ) { 45 | self::addKey( stripcslashes( $val ), false, true ); 46 | $nbr ++; 47 | } 48 | // Clientside 49 | // clienttranslate( "key" ) 50 | preg_match_all( "/clienttranslate\( *(\"(.*?)\") *\)/s", $content, $matches ); 51 | foreach ( $matches [2] as $val ) { 52 | self::addKey( stripcslashes( $val ), true, false ); 53 | $nbr ++; 54 | } 55 | // clienttranslate( 'key' ) 56 | preg_match_all( "/clienttranslate\( *('(.*?)') *\)/s", $content, $matches ); 57 | foreach ( $matches [2] as $val ) { 58 | self::addKey( stripcslashes( $val ), true, false ); 59 | $nbr ++; 60 | } 61 | return $nbr; 62 | } 63 | 64 | /** 65 | * Parse tpl file for server translation keys and write them to the serverside keys destination file. 66 | * @param string $filepath 67 | * @param file handle $OutputFileServer 68 | */ 69 | private function parse_tpl( $file ) { 70 | $this->curfile = $file; 71 | $content = $this->original = file_get_contents( $file ); 72 | $nbr= 0; 73 | // {LB_XX} 74 | preg_match_all( "/\{(LB_[A-Z0-9_]*)\}/", $content, $matches ); 75 | 76 | foreach( $matches[1] as $val ) 77 | { 78 | self::addKey( $val, false, true ); 79 | $nbr++; 80 | } 81 | return $nbr; 82 | } 83 | 84 | function parse_js_fixed($file) { 85 | $this->curfile = $file; 86 | $content = $this->original = file_get_contents( $file ); 87 | // Remove js comments 88 | $content = preg_replace( "/^ *\/\/.*?$/m", "", $content ); 89 | $content = preg_replace( "/\/\*.*?\*\//s", "", $content ); 90 | $nbr = 0; 91 | $nbr += self::parse_single_function( $content, "_", true, false ); 92 | $nbr += self::parse_single_function( $content, "__", true, false, 2 ); 93 | return $nbr; 94 | } 95 | 96 | function parse_php_fixed($file) { 97 | $this->curfile = $file; 98 | $content = $this->original = file_get_contents( $file ); 99 | // Remove php comments 100 | $content = preg_replace( "/^ *\/\/.*?$/m", "", $content ); 101 | $content = preg_replace( "/\/\*.*?\*\//s", "", $content ); 102 | $nbr = 0; 103 | $nbr += self::parse_single_function( $content, "self\\s*::\\s*_", false, true ); 104 | $nbr += self::parse_single_function( $content, "->\\s*_", false, true ); 105 | $nbr += self::parse_single_function( $content, "totranslate", false, true ); 106 | $nbr += self::parse_single_function( $content, "_", false, true ); 107 | $nbr += self::parse_single_function( $content, "clienttranslate", true, false ); 108 | return $nbr; 109 | } 110 | 111 | function parse_single_function(&$content, $func, $bClient, $bServer, $argNum = 1) { 112 | $nbr = 0; 113 | // split file by function, matching word bounder, i.e. no foo_ or x_clienttranslate 114 | $chunks = preg_split( "/\\b${func}\\s*\(\\s*/", $content, - 1, PREG_SPLIT_OFFSET_CAPTURE ); 115 | // first string was not function call, begging of the file, discard it 116 | $first = array_shift( $chunks ); 117 | $this->curline = 0; 118 | $quotes = array ( 119 | "'", 120 | "\"", 121 | "`" 122 | ); 123 | $isPhp = substr($this->curfile,-3,3) == 'php'; 124 | foreach ( $chunks as $funcparsarr ) { 125 | $funcpars = $funcparsarr [0]; 126 | $snippet = "${func}($funcpars"; 127 | $this->curoffset = $funcparsarr [1]; 128 | // echo "->$funcpars\n"; 129 | foreach ( $quotes as $quot ) { 130 | // /"(?:[^"\\]|\\.)*"/ 131 | // Two quotes surrounding zero or more of "any character that's not a quote or a backslash" or 132 | // "a backslash followed by any character". 133 | $regex = "/^${quot}((?:[^${quot}\\\\]|\\\\.)*)${quot}\\s*(.*)/s"; 134 | $res = preg_match( $regex, $funcpars, $matches ); 135 | if ($res === 1) { 136 | break; 137 | } 138 | } 139 | if ($res === 1 && $argNum == 2) { 140 | $res = 0; 141 | $funcpars = $matches [2]; 142 | foreach ( $quotes as $quot ) { 143 | $regex = "/^,\\s*${quot}((?:[^${quot}\\\\]|\\\\.)*)${quot}\\s*(.*)/s"; 144 | $res = preg_match( $regex, $funcpars, $matches ); 145 | if ($res === 1) { 146 | break; 147 | } 148 | } 149 | } 150 | if ($res === 1) { 151 | $val = $matches [1]; 152 | $rest = $matches [2]; 153 | $end = substr( $rest, 0, 1 ); 154 | if (empty( $val )) { 155 | self::printError( "empty string", $snippet ); 156 | } else if ($end != ')') { 157 | self::printError( "unexpected character $end expecting )", $snippet ); 158 | } else if (strstr( $val, '$' ) !== false && $quot == '"') { 159 | if (preg_match( "/.*[^\\\\]\\$/s", $val ) === 1 && $isPhp) { 160 | self::printError( "double quoted string contains unescaped $", $snippet, 'Warning' ); 161 | } 162 | } else if ($quot == '`') { 163 | //self::printError( "avoid using template strings they not supported by many browsers", $snippet, 'Warning' ); 164 | } else if ($func == '_' && $isPhp) { 165 | //self::printError( "avoid using '_' function in php, use '\$this->_' instead", $snippet, 'Warning' ); 166 | } 167 | self::addKey( stripcslashes( $val ), $bClient, $bServer ); 168 | $nbr ++; 169 | } else if ($res === 0) { 170 | // we did not found any suitable pattern, this is bad, report it 171 | self::printError( "invalid translatable string", $snippet ); 172 | } else { 173 | self::printError( "failed to match $regex", $snippet ); 174 | } 175 | } 176 | return $nbr; 177 | } 178 | 179 | function printError($err, $snippet, $severity = 'Error') { 180 | $line = $this->curline; 181 | $pos = $this->curoffset; 182 | if ($line == 0 && $pos == 0) { 183 | $pos = strpos( $this->original, $snippet ); 184 | } 185 | if ($line == 0 && $pos > 0) { 186 | $line = substr_count( substr( $this->original, 0, $pos ), "\n" ); 187 | } 188 | $snippet = preg_replace( "/;.*/s", ";...", $snippet ); 189 | if (strlen( $snippet ) > 100) { 190 | $snippet = substr( $snippet, 0, 100 ) . "..."; 191 | } 192 | echo "$severity:$this->curfile:$line: $err: $snippet\n"; 193 | } 194 | 195 | /** 196 | * Extra function to show untranslated strings 197 | */ 198 | function parse_php_untranslated($file) { 199 | $this->curfile = $file; 200 | $this->original = file_get_contents( $file ); 201 | $this->curoffset = 0; 202 | $this->curline = 0; 203 | self::report_untranslated( $this->original ); 204 | } 205 | 206 | function report_untranslated(&$content) { 207 | $lines = explode( "\n", $content ); 208 | $this->curline = 0; 209 | $quotes = array ( 210 | "'", 211 | "\"" 212 | ); 213 | foreach ( $lines as $line ) { 214 | $line_mod = preg_replace( "/^ *\/\/.*?$/m", "", $line ); 215 | $this->curline ++; 216 | foreach ( $quotes as $quot ) { 217 | // /"(?:[^"\\]|\\.)*"/ 218 | // Two quotes surrounding zero or more of "any character that's not a quote or a backslash" or 219 | // "a backslash followed by any character". 220 | $regex = "/${quot}((?:[^${quot}\\\\]|\\\\.)*)${quot}/s"; 221 | $res = preg_match_all( $regex, $line_mod, $matches ); 222 | foreach ( $matches [1] as $val ) { 223 | $str = stripcslashes( $val ); 224 | if (empty( $str )) 225 | continue; 226 | if (preg_match( "/^[^ ]+$/", $str )) 227 | continue; 228 | if (preg_match( "/^\\W+$/", $str )) 229 | continue; 230 | if (preg_match( "/^[A-Z_]+/", $str )) 231 | continue; 232 | if (preg_match( "/^-/", $str )) 233 | continue; 234 | if (preg_match( "/trace|warn|error|assertTrue/", $line_mod )) 235 | continue; 236 | if (! isset( $this->keys_to_translate [$str] ) && !strstr($line, "NOI18N")) { 237 | self::printError( "Possibly untranslated", "$line", 'Warning' ); 238 | } 239 | } 240 | } 241 | } 242 | } 243 | 244 | function addKey($key, $bClient, $bServer, $bHidden = 0) { 245 | if ($key == '') 246 | return; // Never add an empty key 247 | //echo sprintf( "** '%s' ** => (%b,%b,%b)\n", $key, $bClient, $bServer, $bHidden ); 248 | if (! isset( $this->keys_to_translate [$key] )) 249 | $this->keys_to_translate [$key] = array ( 250 | 's' => 0, 251 | 'c' => 0, 252 | 'h' => 0 253 | ); 254 | if ($bClient) 255 | $this->keys_to_translate [$key] ['c'] = 1; 256 | if ($bServer) 257 | $this->keys_to_translate [$key] ['s'] = 1; 258 | if ($bHidden) 259 | $this->keys_to_translate [$key] ['h'] = 1; 260 | } 261 | 262 | function runTest() { 263 | // $this->parse_php( 'parser_test.php' ); 264 | $this->parse_php_fixed( 'parser_test.php' ); 265 | //$this->parse_js_fixed( 'parser_test.php' ); 266 | } 267 | 268 | function runOnModule($base_path){ 269 | ////////// PHP files //////////// 270 | $files = self::dir_tree( $base_path, ".php" ); 271 | 272 | echoinstant( "" ); 273 | echoinstant( count( $files ). " PHP files to scan:" ); 274 | foreach( $files as $file ) 275 | { 276 | echoinstant( "__".$file ); 277 | $nbr = self::parse_php_fixed( $file ); 278 | echoinstant( "_____".$nbr." strings found"); 279 | self::parse_php_untranslated($file); 280 | } 281 | 282 | ////////// TPL files //////////// 283 | $files = self::dir_tree( $base_path, ".tpl" ); 284 | 285 | echoinstant( "" ); 286 | echoinstant( count( $files ). " TPL files to scan:" ); 287 | foreach( $files as $file ) 288 | { 289 | echoinstant( "__".$file ); 290 | $nbr = self::parse_tpl( $file ); 291 | echoinstant( "_____".$nbr." strings found"); 292 | } 293 | 294 | ////////// JS files //////////// 295 | $files = self::dir_tree( $base_path, ".js" ); 296 | 297 | echoinstant( "" ); 298 | echoinstant( count( $files ). " JS files to scan:" ); 299 | foreach( $files as $file ) 300 | { 301 | echoinstant( "__".$file ); 302 | $nbr = self::parse_js_fixed( $file ); 303 | echoinstant( "_____".$nbr." strings found"); 304 | } 305 | } 306 | 307 | /** 308 | * Returns an array of all files matching the extension in $dir and its subdirectories 309 | * @param string $dir 310 | * @param string $extension 311 | * @return array 312 | */ 313 | private function dir_tree($dir, $extension) { 314 | $path = array(); 315 | $stack[] = $dir; 316 | while ($stack) { 317 | $thisdir = array_pop($stack); 318 | if ($dircont = scandir($thisdir)) { 319 | $i=0; 320 | while (isset($dircont[$i])) { 321 | if ($dircont[$i] !== '.' 322 | && $dircont[$i] !== '..' 323 | && $dircont[$i] !== '.svn' 324 | && $dircont[$i] !== 'translation' 325 | && $dircont[$i] !== 'clienttranslations' 326 | && $dircont[$i] !== 'img' 327 | && $dircont[$i] !== 'games' 328 | && $dircont[$i] !== 'layer' 329 | && $dircont[$i] !== 'dojoroot' 330 | && $dircont[$i] !== 'nls' 331 | && $dircont[$i] !== 'data' // Note: data has some recursion... 332 | && $dircont[$i] !== 'tpl_translate' 333 | ) { 334 | 335 | $current_file = "{$thisdir}/{$dircont[$i]}"; 336 | if (is_file($current_file) 337 | && $dircont[$i] !== 'jstranslations.game.php') { 338 | if ($this->endsWith( $dircont[$i], $extension ) !== FALSE) { 339 | $path[] = $current_file; 340 | } 341 | } elseif (is_dir($current_file)) { 342 | $stack[] = $current_file; 343 | } 344 | } 345 | $i++; 346 | } 347 | } 348 | } 349 | return $path; 350 | } 351 | 352 | function endsWith($haystack, $needle) 353 | { 354 | $length = strlen($needle); 355 | if ($length == 0) { 356 | return true; 357 | } 358 | 359 | return (substr($haystack, -$length) === $needle); 360 | } 361 | } 362 | function echoinstant($msg) { 363 | echo $msg."\n"; 364 | } 365 | $p = new PhpParser(); 366 | //$p->runTest(); 367 | //$p->parse_php_fixed( '../../hive/hive/hive.game.php' ); 368 | //$p->parse_php_untranslated( '../../hive/hive/hive.game.php' ); 369 | 370 | $indir = $argv [1]; 371 | $p->runOnModule($indir); 372 | echo "found " . count( $p->keys_to_translate ) . " keys \n"; 373 | foreach ($p->keys_to_translate as $key => $value) { 374 | echoinstant($key); 375 | } 376 | 377 | -------------------------------------------------------------------------------- /misc/php.txt: -------------------------------------------------------------------------------- 1 | This is reference for public method defined in Table and its ancestors, this information obtained by using php reflection 2 | 3 | == Class hierarchy == 4 | 5 | * APP_Object 6 | ** APP_DbObject 7 | *** APP_GameClass 8 | **** Table 9 | **** [[Deck]] 10 | 11 | 12 | == Methods in the Table class == 13 | 14 | Table::getGameName 15 | Table::_ 16 | Table::Table 17 | Table::setTable 18 | Table::initTable 19 | Table::getAllTableDatas 20 | Table::getAllDatas 21 | Table::setupNewGameTable 22 | Table::gameVersionToDbVersion 23 | Table::setupNewGame 24 | Table::getTableOptionsForGame 25 | Table::getTableOptions 26 | Table::getTablePreferencesForGame 27 | Table::getTablePreferences 28 | Table::getGameInfosForGame 29 | Table::getGameOptionsInfos 30 | Table::start 31 | Table::loadPlayersBasicInfos 32 | Table::reloadPlayersBasicInfos 33 | Table::reattributeColorsBasedOnPreferences 34 | Table::getBestColorFromColorPrefs 35 | Table::initSetupPlayersInfos 36 | Table::getPlayersNumber 37 | Table::checkAction 38 | Table::checkGameStart 39 | Table::color_to_color_back 40 | Table::initGameStateLabels 41 | Table::setGameStateInitialValue 42 | Table::getGameStateValue 43 | Table::setGameStateValue 44 | Table::incGameStateValue 45 | Table::is_testmode 46 | Table::testmodedatas 47 | Table::applyTestModeDbFixture 48 | Table::getActivePlayerId 49 | Table::getActivePlayerName 50 | Table::getCurrentPlayerId 51 | Table::getCurrentPlayerName 52 | Table::getCurrentPlayerColor 53 | Table::isCurrentPlayerZombie 54 | Table::getPlayerCount 55 | Table::createNextPlayerTable 56 | Table::getNextPlayerTable 57 | Table::createPrevPlayerTable 58 | Table::getPrevPlayerTable 59 | Table::getPlayerAfter 60 | Table::getPlayerBefore 61 | Table::activeNextPlayer 62 | Table::activePrevPlayer 63 | Table::forceEndOfGame 64 | Table::giveExtraTime 65 | Table::checkZombieTurn 66 | Table::skipPlayersOutOfTime 67 | Table::onPlayerHasBeenZombified 68 | Table::forceAbandon 69 | Table::zombieBack 70 | Table::aiPlayer 71 | Table::aiNotPlaying 72 | Table::aiError 73 | Table::say 74 | Table::getGameProgression 75 | Table::getStatTypesForGame 76 | Table::getStatTypes 77 | Table::stat_type_id_to_name 78 | Table::initStat 79 | Table::getStat 80 | Table::setStat 81 | Table::setStatForAllPlayers 82 | Table::incStat 83 | Table::getStatFromResult 84 | Table::setStatOnResult 85 | Table::setStatOnResultForPlayer 86 | Table::getStandardGameResultObject 87 | Table::getGameRankInfos 88 | Table::argGameEnd 89 | Table::stGameEnd 90 | Table::stTutorialStart 91 | Table::isSoloGame 92 | Table::notifyAllPlayers 93 | Table::notifyPlayer 94 | Table::onEndAjaxAction 95 | Table::checkReturnState 96 | Table::sendNotifications 97 | Table::sendNotificationPacket 98 | Table::getCurrentNotificationNextNo 99 | Table::getNotificationHistory 100 | Table::debugChat 101 | Table::timeout 102 | Table::eliminatePlayer 103 | Table::isAsync 104 | Table::getTimeLimits 105 | Table::getAsyncTimeLimits 106 | Table::checkAsyncActivePlayersChange 107 | Table::upgradeTableDb 108 | Table::getReplayPoints 109 | Table::saveReplayPoint 110 | Table::undoAndReplayInit 111 | Table::removeAutoIncrementFromTable 112 | Table::getFieldsListOfTable 113 | Table::undoInit 114 | Table::undoSavepoint 115 | Table::doUndoSavePoint 116 | Table::undoRestorePoint 117 | Table::showTutorial 118 | Table::seenTutorial 119 | Table::activeTutorial 120 | Table::forceGameTournamendEnd 121 | Table::showCursor 122 | Deck::Deck 123 | Deck::init 124 | Deck::createCards 125 | Deck::getExtremePosition 126 | Deck::shuffle 127 | Deck::pickCard 128 | Deck::pickCards 129 | Deck::pickCardForLocation 130 | Deck::pickCardsForLocation 131 | Deck::getCardOnTop 132 | Deck::getCardsOnTop 133 | Deck::reformDeckFromDiscard 134 | Deck::moveCard 135 | Deck::moveCards 136 | Deck::insertCard 137 | Deck::insertCardOnExtremePosition 138 | Deck::moveAllCardsInLocation 139 | Deck::moveAllCardsInLocationKeepOrder 140 | Deck::getCardsInLocation 141 | Deck::getPlayerHand 142 | Deck::getCard 143 | Deck::getCards 144 | Deck::getCardsFromLocation 145 | Deck::getCardsOfType 146 | Deck::getCardsOfTypeInLocation 147 | Deck::playCard 148 | Deck::countCardInLocation 149 | Deck::countCardsInLocation 150 | Deck::countCardsInLocations 151 | Deck::countCardsByLocationArgs 152 | APP_GameClass::getNewUnique 153 | APP_GameClass::getNew 154 | APP_GameClass::notifyNow 155 | APP_DbObject::ConnectDb 156 | APP_DbObject::DbQuery 157 | APP_DbObject::DbTraceTimeBefore 158 | APP_DbObject::DbTraceTimeAfter 159 | APP_DbObject::DbGetLastId 160 | APP_DbObject::DbDumpQueryHistory 161 | APP_DbObject::DbAffectedRow 162 | APP_DbObject::DbStartTransaction 163 | APP_DbObject::DbCommit 164 | APP_DbObject::DbRollback 165 | APP_DbObject::DbRestartTransaction 166 | APP_DbObject::DbSelect 167 | APP_DbObject::CommitAllAndRestart 168 | APP_DbObject::setDeadlockMode 169 | APP_DbObject::isDeadlockModeRetry 170 | APP_DbObject::enableMultiQueries 171 | APP_DbObject::sendMultiQueries 172 | APP_DbObject::escapeStringForDB 173 | APP_DbObject::getCollectionFromDB 174 | APP_DbObject::getNonEmptyCollectionFromDB 175 | APP_DbObject::getDoubleKeyCollectionFromDB 176 | APP_DbObject::getUniqueValueFromDB 177 | APP_DbObject::mysql_fetch_row 178 | APP_DbObject::mysql_fetch_assoc 179 | APP_DbObject::mysql_query 180 | APP_DbObject::getObjectFromDB 181 | APP_DbObject::getNonEmptyObjectFromDB 182 | APP_DbObject::getObjectListFromDB 183 | APP_DbObject::getSelectedDb 184 | APP_DbObject::sqlParsing 185 | APP_DbObject::DbUsePrefix 186 | APP_DbObject::applyPrefix 187 | APP_DbObject::cache_store 188 | APP_DbObject::cache_add 189 | APP_DbObject::cache_exists 190 | APP_DbObject::cache_fetch 191 | APP_DbObject::cache_delete 192 | APP_DbObject::cache_rollback 193 | APP_DbObject::cache_commit 194 | APP_DbObject::ensure_enough_time_since_last_action 195 | APP_DbObject::getMasterNodeDomain 196 | APP_DbObject::getMasterNodeUrl 197 | APP_DbObject::masterNodeRequest 198 | APP_DbObject::gameserverNodeRequest 199 | APP_DbObject::gameserverNodeRequestNoTable 200 | APP_DbObject::bgaCallUrl 201 | APP_Object::watch 202 | APP_Object::debug 203 | APP_Object::trace 204 | APP_Object::warn 205 | APP_Object::error 206 | APP_Object::dump 207 | 208 | 209 | 210 | This is example of player array send to setupNewGame php method 211 | array(2) { 212 | [2300663]=> array(7) { 213 | ["player_name"]=> string(8) "laskava1" 214 | ["player_canal"]=> string(32) "aa5ceb9e01f1367d4f4a42eaa535b0f0" 215 | ["player_avatar"]=> string(6) "000000" 216 | ["player_is_admin"]=> string(1) "0" 217 | ["player_is_ai"]=> string(1) "0" 218 | ["player_table_order"]=> string(1) "2" 219 | ["beginner"]=> string(1) "1" 220 | } 221 | [2300662]=> array(8) { 222 | ["player_name"]=> string(8) "laskava0" 223 | ["player_canal"]=> string(32) "60d9d1240f012630bb8f6bf07cbf5139" 224 | ["player_avatar"]=> string(10) "bf78772d42" 225 | ["player_is_admin"]=> string(1) "1" 226 | ["player_is_ai"]=> string(1) "0" 227 | ["player_table_order"]=> string(1) "1" 228 | ["player_colorprefs"]=> array(4) { 229 | [0]=> string(6) "ff0000" 230 | [1]=> string(6) "ffa500" 231 | [2]=> string(6) "000000" 232 | [3]=> string(6) "ffffff" 233 | } 234 | ["beginner"]=> string(1) "0" 235 | } 236 | } {"status":1,"data":true} 237 | -------------------------------------------------------------------------------- /misc/sharedcode.test.php: -------------------------------------------------------------------------------- 1 | resources = array (); 11 | } 12 | // override methods here that access db and stuff 13 | 14 | function getGameStateValue($var) { 15 | if ($var=='round') return 3; 16 | return 0; 17 | } 18 | } 19 | 20 | $x = new SharedCodeTest1(); 21 | $p = $x->getGameProgression(); 22 | if ($p != 50) echo "Test1: FAILED"; 23 | else echo "Test1: PASSED"; 24 | -------------------------------------------------------------------------------- /misc/view/common/game.view.php: -------------------------------------------------------------------------------- 1 | 8 | require_once (APP_GAMEMODULE_PATH . 'module/table/table.game.php'); 9 | require_once ('modules/tokens.php'); 10 | require_once ('modules/APP_Extended.php'); 11 | 12 | class BattleShip extends APP_Extended { 13 | } 14 | 15 | * 16 | */ 17 | define("GS_PLAYER_TURN_NUMBER", 'playerturn_nbr'); 18 | 19 | abstract class APP_Extended extends Table 20 | { 21 | function __construct() 22 | { 23 | parent::__construct(); 24 | } 25 | 26 | /* 27 | * setupNewGame: 28 | * 29 | * This method is called only once, when a new game is launched. 30 | * In this method, you must setup the game according to the game rules, so that 31 | * the game is ready to be played. 32 | */ 33 | protected function setupNewGame($players, $options = array()) 34 | { 35 | /** 36 | * ********** Start the game initialization **** 37 | */ 38 | $this->initPlayers($players); 39 | $this->initStats(); 40 | // Setup the initial game situation here 41 | $this->initTables(); 42 | /** 43 | * ********** End of the game initialization **** 44 | */ 45 | } 46 | 47 | 48 | /** 49 | * override to setup all custom tables 50 | */ 51 | protected function initTables() 52 | { 53 | } 54 | 55 | 56 | 57 | public function initPlayers($players) 58 | { 59 | // Set the colors of the players with HTML color code 60 | // The default below is red/green/blue/orange/brown 61 | // The number of colors defined here must correspond to the maximum number of players allowed for the gams 62 | $gameinfos = self::getGameinfos(); 63 | $default_colors = $gameinfos['player_colors']; 64 | shuffle($default_colors); 65 | // Create players 66 | // Note: if you added some extra field on "player" table in the database (dbmodel.sql), you can initialize it there. 67 | $sql = "INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar) VALUES "; 68 | $values = array(); 69 | foreach ($players as $player_id => $player) { 70 | $color = array_shift($default_colors); 71 | $values[] = "('" . $player_id . "','$color','" . $player['player_canal'] . "','" . addslashes($player['player_name']) . "','" . addslashes($player['player_avatar']) . "')"; 72 | } 73 | $sql .= implode(',', $values); 74 | self::DbQuery($sql); 75 | if ($gameinfos['favorite_colors_support']) 76 | self::reattributeColorsBasedOnPreferences($players, $gameinfos['player_colors']); 77 | self::reloadPlayersBasicInfos(); 78 | $this->activeNextPlayer(); // just in case so its not 0, dev code can change it later 79 | } 80 | 81 | public function initStats() 82 | { 83 | // INIT GAME STATISTIC 84 | $all_stats = $this->getStatTypes(); 85 | $player_stats = $all_stats['player']; 86 | // auto-initialize all stats that starts with game_ 87 | // we need a prefix because there is some other system stuff 88 | foreach ($player_stats as $key => $value) { 89 | if (startsWith($key, 'game_')) { 90 | $this->initStat('player', $key, 0); 91 | } 92 | if ($key === 'turns_number') { 93 | $this->initStat('player', $key, 0); 94 | } 95 | } 96 | $table_stats = $all_stats['table']; 97 | foreach ($table_stats as $key => $value) { 98 | if (startsWith($key, 'game_')) { 99 | $this->initStat('table', $key, 0); 100 | } 101 | if ($key === 'turns_number') { 102 | $this->initStat('table', $key, 0); 103 | } 104 | } 105 | } 106 | 107 | // ------ ERROR HANDLING ---------- 108 | /** 109 | * This will throw an exception if condition is false. 110 | * The message should be translated and shown to the user. 111 | * 112 | * @param $message string 113 | * user side error message, translation is needed, use self::_() when passing string to it 114 | * @param $cond boolean condition of assert 115 | * @param $log string optional log message, not need to translate 116 | * @throws BgaUserException 117 | */ 118 | function userAssertTrue($message, $cond = false, $log = "") 119 | { 120 | if ($cond) 121 | return; 122 | if ($log) 123 | $this->warn("$message $log|"); 124 | throw new BgaUserException($message); 125 | } 126 | 127 | /** 128 | * This will throw an exception if condition is false. 129 | * This only can happened if user hacks the game, client must prevent this 130 | * 131 | * @param string $log 132 | * server side log message, no translation needed 133 | * @param bool $cond 134 | * condition of assert 135 | * @throws BgaUserException 136 | */ 137 | function systemAssertTrue($log, $cond = false) 138 | { 139 | if ($cond) 140 | return; 141 | $move = $this->getGameStateValue('playerturn_nbr'); 142 | $this->error("Internal Error during move $move: $log|"); 143 | $e = new Exception($log); 144 | $this->error($e->getTraceAsString()); 145 | throw new BgaUserException($this->_("Internal Error. That should not have happened. Please raise a bug.")); 146 | } 147 | 148 | // ------ NOTIFICATIONS ---------- 149 | function notifyWithName($type, $message = '', $args = null, $player_id = -1) 150 | { 151 | if ($args == null) 152 | $args = array(); 153 | $this->systemAssertTrue("Invalid notification signature", is_array($args)); 154 | if (array_key_exists('player_id', $args) && $player_id == -1) { 155 | $player_id = $args['player_id']; 156 | } 157 | if ($player_id == -1) 158 | $player_id = $this->getMostlyActivePlayerId(); 159 | if ($player_id != 'all') 160 | $args['player_id'] = $player_id; 161 | if ($message) { 162 | $player_name = $this->getPlayerNameById($player_id); 163 | $args['player_name'] = $player_name; 164 | } 165 | if (array_key_exists('_notifType', $args)) { 166 | $type = $args['_notifType']; 167 | unset($args['_notifType']); 168 | } 169 | if (array_key_exists('noa', $args) || array_key_exists('nop', $args) || array_key_exists('nod', $args)) { 170 | $type .= "Async"; 171 | } 172 | if (array_key_exists('_private', $args) && $args['_private']) { 173 | unset($args['_private']); 174 | $this->notifyPlayer($player_id, $type, $message, $args); 175 | } else { 176 | $this->notifyAllPlayers($type, $message, $args); 177 | } 178 | } 179 | 180 | function getMostlyActivePlayerId() 181 | { 182 | $state = $this->gamestate->state(); 183 | if ($state['type'] === "multipleactiveplayer") { 184 | return $this->getCurrentPlayerId(); 185 | } else { 186 | return $this->getActivePlayerId(); 187 | } 188 | } 189 | function notifyAnimate() 190 | { 191 | $this->notifyAllPlayers('animate', '', array()); 192 | } 193 | 194 | function notifyAction($action_id) 195 | { 196 | //$this->notifyMoveNumber(); 197 | $this->notifyWithName('playerLog', clienttranslate('${player_name} took action ${token_name}'), array( 198 | 'token_id' => $action_id, 'token_name' => $action_id 199 | )); 200 | } 201 | 202 | // ------ PLAYERS ---------- 203 | /** 204 | * 205 | * @return integer first player in natural player order 206 | */ 207 | function getFirstPlayer() 208 | { 209 | $table = $this->getNextPlayerTable(); 210 | return $table[0]; 211 | } 212 | 213 | /** 214 | * 215 | * @return string hex color as in players table for the player with $player_id 216 | */ 217 | function getPlayerColor($player_id) 218 | { 219 | $players = $this->loadPlayersBasicInfos(); 220 | if (!isset($players[$player_id])) { 221 | return 0; 222 | } 223 | return $players[$player_id]['player_color']; 224 | } 225 | 226 | /** 227 | * 228 | * @return integer player id based on hex $color 229 | */ 230 | function getPlayerIdByColor($color) 231 | { 232 | $players = $this->loadPlayersBasicInfos(); 233 | if (!isset($this->player_colors)) { 234 | $this->player_colors = array(); 235 | foreach ($players as $player_id => $info) { 236 | $this->player_colors[$info['player_color']] = $player_id; 237 | } 238 | } 239 | if (!isset($this->player_colors[$color])) { 240 | return 0; 241 | } 242 | return $this->player_colors[$color]; 243 | } 244 | 245 | /** 246 | * 247 | * @return integer player position (as player_no) from database 248 | */ 249 | function getPlayerPosition($player_id) 250 | { 251 | $players = $this->loadPlayersBasicInfos(); 252 | if (!isset($players[$player_id])) { 253 | return -1; 254 | } 255 | return $players[$player_id]['player_no']; 256 | } 257 | 258 | public function getStateName() 259 | { 260 | $state = $this->gamestate->state(); 261 | return $state['name']; 262 | } 263 | 264 | /** 265 | * @return array of player ids 266 | */ 267 | function getPlayerIds() 268 | { 269 | $players = $this->loadPlayersBasicInfos(); 270 | return array_keys($players); 271 | } 272 | 273 | function getPlayerIdsInOrder($starting) 274 | { 275 | $player_ids = $this->getPlayerIds(); 276 | $rotate_count = array_search($starting, $player_ids); 277 | if ($rotate_count === false) { 278 | return $player_ids; 279 | } 280 | for ($i = 0; $i < $rotate_count; $i++) { 281 | array_push($player_ids, array_shift($player_ids)); 282 | } 283 | return $player_ids; 284 | } 285 | 286 | /** 287 | * Return player table in order starting from $staring player id, if $starting is not in the player table 288 | * i.e. spectator returns same as loadPlayersBasicInfos(), i.e. natural player order 289 | * This is useful in view.php file 290 | * @param number $starting - player number 291 | * @return string[][] - map of playerId => playerInfo 292 | */ 293 | function getPlayersInOrder($starting) 294 | { 295 | $players = $this->loadPlayersBasicInfos(); 296 | $player_ids = $this->getPlayerIdsInOrder($starting); 297 | $result = []; 298 | foreach ($player_ids as $player_id) { 299 | $result[$player_id] = $players[$player_id]; 300 | } 301 | return $result; 302 | } 303 | 304 | function debugConsole($info, $args = array()) 305 | { 306 | $this->notifyAllPlayers("log", $info, $args); 307 | $this->warn($info); 308 | } 309 | 310 | /** 311 | * Change activate player, also increasing turns_number stats and giving extra time 312 | */ 313 | function setNextActivePlayerCustom($next_player_id) 314 | { 315 | if ($this->getActivePlayerId() == $next_player_id) 316 | return; 317 | $this->giveExtraTime($next_player_id); 318 | $this->incStat(1, 'turns_number', $next_player_id); 319 | $this->incStat(1, 'turns_number'); 320 | $this->gamestate->changeActivePlayer($next_player_id); 321 | } 322 | 323 | // ------ DB ---------- 324 | function dbGetScore($player_id) 325 | { 326 | return $this->getUniqueValueFromDB("SELECT player_score FROM player WHERE player_id='$player_id'"); 327 | } 328 | 329 | function dbSetScore($player_id, $count) 330 | { 331 | $this->DbQuery("UPDATE player SET player_score='$count' WHERE player_id='$player_id'"); 332 | } 333 | 334 | function dbSetAuxScore($player_id, $score) 335 | { 336 | $this->DbQuery("UPDATE player SET player_score_aux=$score WHERE player_id='$player_id'"); 337 | } 338 | 339 | function dbIncScore($player_id, $inc) 340 | { 341 | $count = $this->dbGetScore($player_id); 342 | if ($inc != 0) { 343 | $count += $inc; 344 | $this->dbSetScore($player_id, $count); 345 | } 346 | return $count; 347 | } 348 | 349 | /** 350 | * Changes the player scrore and sends notification, also update statistic if provided 351 | * 352 | * @param number $player_id 353 | * - player id 354 | * @param number $inc 355 | * - increment of score, can be negative 356 | * @param string $notif 357 | * - notification string, '*' - for default notification, '' - for none 358 | * @param string $stat 359 | * - name of the player statistic to update (points source) 360 | * @return number - current score after increase/descrease 361 | */ 362 | function dbIncScoreValueAndNotify($player_id, $inc, $notif = '*', $stat = '', $args = null) 363 | { 364 | if ($args == null) 365 | $args = []; 366 | $count = $this->dbIncScore($player_id, $inc); 367 | if ($notif == '*') { 368 | if ($inc >= 0) 369 | $notif = clienttranslate('${player_name} scores ${inc} point(s)'); 370 | else 371 | $notif = clienttranslate('${player_name} loses ${mod} point(s)'); 372 | } 373 | $this->notifyWithName( 374 | "score", 375 | $notif, // 376 | array_merge(array('player_score' => $count, 'inc' => $inc, 'mod' => abs($inc)), $args), // 377 | $player_id 378 | ); 379 | if ($stat) { 380 | $this->dbIncStatChecked($inc, $stat, $player_id); 381 | } 382 | return $count; 383 | } 384 | 385 | function dbIncStatChecked($inc, $stat, $player_id) 386 | { 387 | try { 388 | $all_stats = $this->getStatTypes(); 389 | $player_stats = $all_stats['player']; 390 | if (isset($player_stats[$stat])) { 391 | $this->incStat($inc, $stat, $player_id); 392 | } else { 393 | $this->error("statistic $stat is not defined"); 394 | } 395 | } catch (Exception $e) { 396 | $this->error("error while setting statistic $stat"); 397 | $this->dump('err', $e); 398 | } 399 | } 400 | 401 | 402 | /** 403 | * Changes values of multiactivity in db, does not sent notifications. 404 | * To send notifications after use updateMultiactiveOrNextState 405 | * @param number $player_id, player id <=0 or null - means ALL 406 | * @param number $value - 1 multiactive, 0 non multiactive 407 | */ 408 | function dbSetPlayerMultiactive($player_id = -1, $value = 1) 409 | { 410 | if (!$value) 411 | $value = 0; 412 | else 413 | $value = 1; 414 | $sql = "UPDATE player SET player_is_multiactive = '$value' WHERE player_zombie = 0 and player_eliminated = 0"; 415 | if ($player_id > 0) { 416 | $sql .= " AND player_id = $player_id"; 417 | } 418 | self::DbQuery($sql); 419 | } 420 | 421 | function isPlayerMaskSet($player_id, $variable) 422 | { 423 | $mask = $this->getGameStateValue($variable); 424 | $no = $this->getPlayerPosition($player_id); 425 | $bit = (1 << $no); 426 | if (($mask & $bit) == 0) { 427 | return false; 428 | } else { 429 | return true; 430 | } 431 | } 432 | 433 | function setPlayerMask($player_id, $variable, $force = false) 434 | { 435 | $mask = $this->getGameStateValue($variable); 436 | $no = $this->getPlayerPosition($player_id); 437 | $bit = (1 << $no); 438 | if ($force || ($mask & $bit) == 0) { 439 | $mask |= $bit; // set the bit 440 | } else { 441 | $this->systemAssertTrue("Player already has this mask set for $variable"); 442 | } 443 | $this->setGameStateValue($variable, $mask); 444 | } 445 | 446 | function clearPlayerMask($player_id, $variable) 447 | { 448 | $mask = $this->getGameStateValue($variable); 449 | $no = $this->getPlayerPosition($player_id); 450 | $bit = (1 << $no); 451 | $mask &= ~$bit; // clear bit 452 | $this->setGameStateValue($variable, $mask); 453 | } 454 | } 455 | 456 | // GLOBAL utility functions 457 | 458 | function startsWith($haystack, $needle) 459 | { 460 | // search backwards starting from haystack length characters from the end 461 | return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false; 462 | } 463 | 464 | function endsWith($haystack, $needle) 465 | { 466 | $length = strlen($needle); 467 | return $length === 0 || (substr($haystack, -$length) === $needle); 468 | } 469 | 470 | function getPart($haystack, $i, $bNoexeption = false) 471 | { 472 | $parts = explode('_', $haystack); 473 | $len = count($parts); 474 | if ($bNoexeption && $i >= $len) 475 | return ""; 476 | return $parts[$i]; 477 | } 478 | 479 | function getPartsPrefix($haystack, $i) 480 | { 481 | $parts = explode('_', $haystack); 482 | $len = count($parts); 483 | if ($i < 0) { 484 | $i = $len + $i; 485 | } 486 | if ($i <= 0) 487 | return ''; 488 | for (; $i < $len; $i++) { 489 | unset($parts[$i]); 490 | } 491 | return implode('_', $parts); 492 | } 493 | 494 | if (!function_exists('array_key_first')) { 495 | function array_key_first(array $arr) 496 | { 497 | foreach ($arr as $key => $unused) { 498 | return $key; 499 | } 500 | return NULL; 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /modules/EuroGame.php: -------------------------------------------------------------------------------- 1 | 6 | require_once (APP_GAMEMODULE_PATH . 'module/table/table.game.php'); 7 | 8 | require_once ('modules/EuroGame.php'); 9 | 10 | class EpicKingdom extends EuroGame { 11 | } 12 | 13 | * 14 | */ 15 | require_once ('APP_Extended.php'); 16 | require_once ('tokens.php'); 17 | 18 | abstract class EuroGame extends APP_Extended { 19 | public $tokens; 20 | public $token_types; 21 | 22 | public function __construct() { 23 | parent::__construct(); 24 | $this->tokens = new Tokens(); 25 | } 26 | 27 | protected function setCounter(&$array, $key, $value) { 28 | $array [$key] = array ('counter_value' => $value,'counter_name' => $key ); 29 | } 30 | 31 | protected function fillCounters(&$array, $locs, $create = true) { 32 | foreach ( $locs as $location => $count ) { 33 | $key = $location . "_counter"; 34 | if ($create || array_key_exists($key, $array)) 35 | $this->setCounter($array, $key, $count); 36 | } 37 | } 38 | 39 | protected function fillTokensFromArray(&$array, $cards) { 40 | foreach ( $cards as $pos => $card ) { 41 | $id = $card ['key']; 42 | $array [$id] = $card; 43 | } 44 | } 45 | 46 | protected function getAllDatas() { 47 | $result = array (); 48 | $current_player_id = self::getCurrentPlayerId(); // !! We must only return informations visible by this player !! 49 | // Get information about players 50 | // Note: you can retrieve some extra field you added for "player" table in "dbmodel.sql" if you need it. 51 | $sql = "SELECT player_id id, player_score score, player_no no FROM player "; 52 | $result ['players'] = self::getCollectionFromDb($sql); 53 | $result ['token_types'] = $this->token_types; 54 | $result ['tokens'] = array (); 55 | $result ['counters'] = $this->getDefaultCounters(); 56 | $locs = $this->tokens->countTokensInLocations(); 57 | //$color = $this->getPlayerColor($current_player_id); 58 | foreach ( $locs as $location => $count ) { 59 | $info = $this->token_types [$location] ?? array (); 60 | $sort = $info ['sort'] ?? null; 61 | 62 | if ($this->isCounterAllowedForLocation($current_player_id, $location)) { 63 | $this->fillCounters($result ['counters'], [ $location => $count ]); 64 | } 65 | $content = $this->isContentAllowedForLocation($current_player_id, $location); 66 | if ($content !== false) { 67 | if ($content === true) { 68 | $tokens = $this->tokens->getTokensInLocation($location, null, $sort); 69 | $this->fillTokensFromArray($result ['tokens'], $tokens); 70 | } else { 71 | $num = floor($content); 72 | if ($count < $num) 73 | $num = $count; 74 | $tokens = $this->tokens->getTokensOnTop($num, $location); 75 | $this->fillTokensFromArray($result ['tokens'], $tokens); 76 | } 77 | } 78 | } 79 | $table_options = $this->getTableOptions(); 80 | $result ['table_options'] = []; 81 | foreach ( $table_options as $option_id => $option ) { 82 | $value = 0; 83 | if (array_key_exists($option_id, $this->gamestate->table_globals)) { 84 | $value = (int) $this->gamestate->table_globals [$option_id]; 85 | } 86 | $result ['table_options'] [$option_id] = $option; 87 | $result ['table_options'] [$option_id] ['value'] = $value; 88 | } 89 | 90 | return $result; 91 | } 92 | 93 | protected function getDefaultCounters() { 94 | $types = $this->token_types; 95 | $res = [ ]; 96 | $players_basic = $this->loadPlayersBasicInfos(); 97 | foreach ( $types as $key => $info ) { 98 | if (array_key_exists('loc', $info) && $info ['loc'] && $info ['counter'] == 1) { 99 | if ($info ['loc'] == 1) { 100 | $this->setCounter($res, "${key}_counter", 0); 101 | } else if ($info ['loc'] == 2) { 102 | foreach ( $players_basic as $player_id => $player_info ) { 103 | $color = $player_info ['player_color']; 104 | $this->setCounter($res, "${key}_${color}_counter", 0); 105 | } 106 | } 107 | } 108 | } 109 | return $res; 110 | } 111 | 112 | protected function isContentAllowedForLocation($player_id, $location) { 113 | if ($location === 'dev_null') 114 | return false; 115 | $key = $location; 116 | $attr = 'content'; 117 | if (! array_key_exists($key, $this->token_types)) { 118 | $key = getPartsPrefix($location, - 1); 119 | } 120 | if (array_key_exists($key, $this->token_types)) { 121 | $info = $this->token_types [$key]; 122 | if (array_key_exists('loc', $info) && $info ['loc']) { 123 | if ($info [$attr] == 1) { 124 | return true; 125 | } 126 | if ($info [$attr] == 2) { 127 | $color = $this->getPlayerColor($player_id); 128 | return endsWith($location, $color); 129 | } 130 | } else { 131 | return true; // not listed as location 132 | } 133 | } else { 134 | return true; // not listed allowed 135 | } 136 | return false; 137 | } 138 | 139 | protected function isCounterAllowedForLocation($player_id, $location) { 140 | if ($location === 'dev_null') 141 | return false; 142 | $key = $location; 143 | $attr = 'counter'; 144 | if (! array_key_exists($key, $this->token_types)) { 145 | $key = getPartsPrefix($location, - 1); 146 | } 147 | if (array_key_exists($key, $this->token_types)) { 148 | $info = $this->token_types [$key]; 149 | if (array_key_exists('loc', $info) && $info ['loc']) { 150 | if ($info [$attr] == 1) { 151 | return true; 152 | } 153 | if ($info [$attr] == 2) { 154 | $color = $this->getPlayerColor($player_id); 155 | return endsWith($location, $color); 156 | } 157 | } 158 | } 159 | return false; 160 | } 161 | 162 | function dbSetTokenState($token_id, $state = null, $notif = '*', $args = null) { 163 | $this->dbSetTokenLocation($token_id, null, $state, $notif, $args); 164 | } 165 | 166 | function dbSetTokenLocation($token_id, $place_id, $state = null, $notif = '*', $args = null) { 167 | $this->systemAssertTrue("token_id is null/empty $token_id, $place_id $notif", $token_id != null && $token_id != ''); 168 | if ($args == null) 169 | $args = array (); 170 | if ($notif === '*') 171 | $notif = clienttranslate('${player_name} moves ${token_name} into ${place_name}'); 172 | if ($state === null) { 173 | $state = $this->tokens->getTokenState($token_id); 174 | } 175 | $place_from = $this->tokens->getTokenLocation($token_id); 176 | $this->systemAssertTrue("token_id does not exists, create first: $token_id", $place_from); 177 | if ($place_id === null) { 178 | $place_id = $place_from; 179 | } 180 | $this->tokens->moveToken($token_id, $place_id, $state); 181 | $notifyArgs = array ( 182 | 'token_id' => $token_id, 183 | 'place_id' => $place_id, 184 | 'token_name' => $token_id, 185 | 'place_name' => $place_id, 186 | 'new_state' => $state ); 187 | $args = array_merge($notifyArgs, $args); 188 | //$this->warn("$type $notif ".$args['token_id']." -> ".$args['place_id']."|"); 189 | if (array_key_exists('player_id', $args)) { 190 | $player_id = $args ['player_id']; 191 | } else { 192 | $player_id = $this->getActivePlayerId(); 193 | } 194 | 195 | if (strstr($notif, '${you}')) { 196 | $notifyArgs ['you'] = 'You';// translated on client side, this is for replay after 197 | } 198 | 199 | $this->notifyWithName("tokenMoved", $notif, $args, $player_id); 200 | if ($this->isCounterAllowedForLocation($player_id, $place_from)) { 201 | $this->notifyCounter($place_from, [ 'nod' => true ]); 202 | } 203 | if ($place_id != $place_from && $this->isCounterAllowedForLocation($player_id, $place_id)) { 204 | $this->notifyCounter($place_id, [ 'nod' => true ]); 205 | } 206 | } 207 | 208 | /** 209 | * This method will increase/descrease resource counter (as state) 210 | * @param string $token_id - token key 211 | * @param int $num - increment of the change 212 | * @param string $place - optional $place, only used in notification to show where "resource" 213 | * is gain or where it "goes" when its paid, used in client for animation 214 | */ 215 | function dbResourceInc($token_id, $num, $place = null) { 216 | $player_id = $this->getActivePlayerId(); 217 | $color = $this->getPlayerColor($player_id); 218 | 219 | $current = $this->tokens->getTokenState($token_id); 220 | $value = $this->tokens->setTokenState($token_id, $current + $num); 221 | if ($value < 0) { 222 | $this->userAssertTrue(self::_("Not enough resources to pay"), $current >= - $num); 223 | } 224 | 225 | if ($num < 0) { 226 | if ($place) 227 | $message = clienttranslate('${player_name} pays ${inc_resource} for ${place_name}'); 228 | else 229 | $message = clienttranslate('${player_name} pays ${inc_resource}'); 230 | } else { 231 | if ($place) 232 | $message = clienttranslate('${player_name} gains ${inc_resource} from ${place_name}'); 233 | else 234 | $message = clienttranslate('${player_name} gains ${inc_resource}'); 235 | } 236 | //$this->warn("playing inc $token_id, $num, $place"); 237 | $this->notifyWithName("counter", $message, [ 238 | 'counter_name'=>$token_id, 239 | 'counter_value'=>$value, 240 | 'place'=>$place, 241 | 'place_name'=>$place, 242 | 'mod' => abs($num), 243 | 'inc' => $num, 244 | 'inc_resource' => [ 'log' => '${token_name} x${mod}', 245 | 'args' => [ 'token_name' => $token_id, 246 | 'mod' => abs($num), 247 | 'inc' => $num, 248 | ] ] 249 | 250 | ]); 251 | } 252 | 253 | function notifyCounter($location, $notifyArgs = null) { 254 | $key = $location . "_counter"; 255 | $value = ($this->tokens->countTokensInLocation($location)); 256 | $this->notifyCounterDirect($key, $value, '', $notifyArgs); 257 | } 258 | 259 | function notifyCounterDirect($key, $value, $message, $notifyArgs = null) { 260 | $args = [ 'counter_name' => $key,'counter_value' => $value ]; 261 | if ($notifyArgs != null) 262 | $args = array_merge($notifyArgs, $args); 263 | $this->notifyWithName("counter", $message, $args); 264 | } 265 | 266 | function notifyCounterInc($key, $value, $message, $notifyArgs = null) { 267 | $args = [ 'counter_name' => $key,'counter_inc' => $value ]; 268 | if ($notifyArgs != null) 269 | $args = array_merge($notifyArgs, $args); 270 | $this->notifyWithName("counter", $message, $args); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /sharedcode.action.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 6 | * SharedCode implementation : © Alena Laskavaia 7 | * 8 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 9 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 10 | * ----- 11 | * 12 | * sharedcode.action.php 13 | * 14 | * SharedCode main action entry point 15 | * 16 | * 17 | * In this file, you are describing all the methods that can be called from your 18 | * user interface logic (javascript). 19 | * 20 | * If you define a method "myAction" here, then you can call it from your javascript code with: 21 | * this.ajaxcall( "/sharedcode/sharedcode/myAction.html", ...) 22 | * 23 | */ 24 | class action_sharedcode extends APP_GameAction { 25 | // Constructor: please do not modify 26 | public function __default() { 27 | if (self::isArg('notifwindow')) { 28 | $this->view = "common_notifwindow"; 29 | $this->viewArgs ['table'] = self::getArg("table", AT_posint, true); 30 | } else { 31 | $this->view = "sharedcode_sharedcode"; 32 | self::trace("Complete reinitialization of board game"); 33 | } 34 | } 35 | 36 | public function selectWorkerAction() { 37 | self::setAjaxMode(); 38 | $arg1 = self::getArg("action_id", AT_alphanum, true); 39 | $arg2 = self::getArg("worker_id", AT_alphanum, true); 40 | $this->game->action_selectWorkerAction($arg1, $arg2); 41 | self::ajaxResponse(); 42 | } 43 | 44 | public function takeCube() { 45 | self::setAjaxMode(); 46 | $arg1 = self::getArg("token_id", AT_alphanum, true); 47 | $arg2 = self::getArg("place_id", AT_alphanum, false); 48 | $this->game->action_takeCube($arg1, $arg2); 49 | self::ajaxResponse(); 50 | } 51 | 52 | public function moveCube() { 53 | self::setAjaxMode(); 54 | $arg1 = self::getArg("token_id", AT_alphanum, true); 55 | $arg2 = self::getArg("place_id", AT_alphanum, true); 56 | $this->game->action_moveCube($arg1, $arg2); 57 | self::ajaxResponse(); 58 | } 59 | 60 | public function drawCard() { 61 | self::setAjaxMode(); 62 | $this->game->action_drawCard(); 63 | self::ajaxResponse(); 64 | } 65 | 66 | public function playCard() { 67 | self::setAjaxMode(); 68 | $arg1 = self::getArg("card_id", AT_alphanum, true); 69 | $this->game->action_playCard($arg1); 70 | self::ajaxResponse(); 71 | } 72 | 73 | 74 | public function pass() { 75 | self::setAjaxMode(); 76 | $this->game->action_pass(); 77 | self::ajaxResponse(); 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /sharedcode.css: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Board Game Arena 3 | All rights reserved. This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License v1.0 which 5 | accompanies this distribution, and is available at 6 | http://www.eclipse.org/legal/epl-v10.html 7 | 8 | Contributors: 9 | BGA framework: © Gregory Isabelli & Emmanuel Colin 10 | Alena Laskavaia - initial API and implementation 11 | 12 | This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 13 | See http://en.boardgamearena.com/#!doc/Studio for more information. 14 | */ 15 | @import url(../../../css/csslayer.css); 16 | /* Note: you must not use any @import directive other than this one */ 17 | 18 | /********* You can start writing your CSS below this line: **********/ 19 | 20 | /** Shadow for square tokens or boards, make them appear more real board-gamish */ 21 | .shadow { 22 | -moz-box-shadow: 1px 1px 2px 1px #222; 23 | -webkit-box-shadow: 1px 1px 2px 1px #222; 24 | -o-box-shadow: 1px 1px 2px 1px #222; 25 | box-shadow: 1px 1px 2px 1px #222; 26 | } 27 | 28 | .notransition { 29 | -webkit-transition: none !important; 30 | -moz-transition: none !important; 31 | -o-transition: none !important; 32 | transition: none !important; 33 | } 34 | 35 | .selected { 36 | outline-color: blue !important; 37 | box-shadow: 1px 1px 2px 1px #444, 0 0 5px blue, 0 0 5px darkblue !important; 38 | } 39 | 40 | .active_slot:hover { 41 | outline-color: blue; 42 | outline-style: solid; 43 | outline-width: 2px; 44 | } 45 | 46 | .active_slot { 47 | cursor: pointer; 48 | outline-color: white; 49 | outline-style: solid; 50 | outline-width: 2px; 51 | } 52 | 53 | .splabel { 54 | position: absolute; 55 | } 56 | 57 | /** mini board styles */ 58 | .mini_board_item { 59 | display: inline-block; 60 | margin-right: 1em; 61 | } 62 | 63 | .mini_board_item>* { 64 | display: inline-block; 65 | vertical-align: middle; 66 | } 67 | 68 | /** Wooden cubes */ 69 | .wcube { 70 | width: 30px; 71 | height: 30px; 72 | background-image: url(img/30_30_wooden_cubes.png); 73 | } 74 | 75 | .wcube_gray { 76 | background-position: 0% 0%; 77 | } 78 | 79 | .wcube_blue { 80 | background-position: 10% 0%; 81 | } 82 | 83 | .wcube_red { 84 | background-position: 20% 0%; 85 | } 86 | 87 | .wcube_yellow { 88 | background-position: 30% 0%; 89 | } 90 | 91 | .wcube_green { 92 | background-position: 40% 0%; 93 | } 94 | 95 | .wcube_white { 96 | background-position: 50% 0%; 97 | } 98 | 99 | .wcube_black { 100 | background-position: 60% 0%; 101 | } 102 | 103 | .wcube_brown { 104 | background-position: 70% 0%; 105 | } 106 | 107 | .wcube_purple { 108 | background-position: 80% 0%; 109 | } 110 | 111 | .wcube_pink { 112 | background-position: 90% 0%; 113 | } 114 | 115 | .wcube_orange { 116 | background-position: 100% 0%; 117 | } 118 | 119 | /** Standing meeples */ 120 | .smeeple2 { 121 | width: 64px; 122 | height: 78px; 123 | background-image: url(img/78_64_stand_meeples.png); 124 | } 125 | 126 | .smeeple { 127 | width: 32px; 128 | height: 39px; 129 | background-image: url(img/78_64_stand_meeples.png); 130 | background-size: 352px; 131 | } 132 | 133 | .smeeple_gray { 134 | background-position: 20% 0%; 135 | } 136 | 137 | .smeeple_blue { 138 | background-position: 40% 0%; 139 | } 140 | 141 | .smeeple_red { 142 | background-position: 30% 0%; 143 | } 144 | 145 | .smeeple_yellow { 146 | background-position: 10% 0%; 147 | } 148 | 149 | .smeeple_green { 150 | background-position: 60% 0%; 151 | } 152 | 153 | .smeeple_white { 154 | background-position: 20% 0%; 155 | } 156 | 157 | .smeeple_black { 158 | background-position: 50% 0%; 159 | } 160 | 161 | .smeeple_brown { 162 | background-position: 50% 0%; 163 | } 164 | 165 | .smeeple_purple { 166 | background-position: 0% 0%; 167 | } 168 | 169 | .smeeple_pink { 170 | background-position: 0% 0%; 171 | } 172 | 173 | .smeeple_orange { 174 | background-position: 70% 0%; 175 | } 176 | 177 | .logitem { 178 | display: inline-block; 179 | vertical-align: middle; 180 | } 181 | 182 | /** main board styles */ 183 | .mainboard { 184 | width: 980px; 185 | height: 300px; 186 | background-color: #c99d6c; 187 | position: relative; 188 | margin-bottom: 10px; 189 | } 190 | 191 | .action_space { 192 | width: 150px; 193 | height: 50px; 194 | background-color: white; 195 | border-style: dotted; 196 | border-width: 1px; 197 | border-color: brown; 198 | position: absolute; 199 | top: 20px; 200 | } 201 | 202 | .action_space_1 { 203 | left: 10px; 204 | } 205 | 206 | .action_space_2 { 207 | left: 180px; 208 | } 209 | 210 | .action_space_3 { 211 | left: 410px; 212 | } 213 | 214 | .action_space_4 { 215 | left: 600px; 216 | } 217 | 218 | .action_space_4::after { 219 | content: "This is text inject by css ::after" 220 | } 221 | 222 | 223 | .action_space>* { 224 | display: inline-block; 225 | } 226 | 227 | .action_space>*:NOT(span) { 228 | z-index: 11; 229 | position: relative; 230 | } 231 | 232 | .basket { 233 | width: 200px; 234 | height: 100px; 235 | background-color: antiquewhite; 236 | outline-style: inset; 237 | outline-width: 3px; 238 | outline-color: white; 239 | position: absolute; 240 | outline-width: 3px; 241 | outline-color: white; 242 | } 243 | 244 | .basket_1 { 245 | top: 180px; 246 | left: 20px; 247 | } 248 | 249 | .basket_2 { 250 | top: 180px; 251 | left: 300px; 252 | } 253 | 254 | .basket>* { 255 | display: inline-block; 256 | } 257 | .funcontrols{ 258 | height: 350px; 259 | } 260 | .funcontrols>*{ 261 | display: inline-block; 262 | margin-right: 30px; 263 | } 264 | 265 | /** Stock areas */ 266 | .playarea, .hand { 267 | min-width: 70px; 268 | min-height: 78px; 269 | padding: 10px; 270 | 271 | } 272 | 273 | .cardgame { 274 | position: relative; 275 | } 276 | .cardgame > * { 277 | display: inline-block; 278 | vertical-align: top; 279 | position: relative; 280 | width: calc(64px * 5); 281 | margin-right: 20px; 282 | } 283 | 284 | label { 285 | white-space: nowrap; 286 | overflow-wrap: none; 287 | display:inline-block; 288 | } 289 | 290 | /** Card flipping effect */ 291 | 292 | /* entire container, keeps perspective */ 293 | .flip-container { 294 | perspective: 1000px; 295 | } 296 | /* flip the pane when hovered */ 297 | .flip-container:hover .flipper, .flip-container.hover .flipper { 298 | transform: rotateY(180deg); 299 | } 300 | 301 | .flip-container, .front, .back { 302 | width: 110px; 303 | height: 176px; 304 | } 305 | 306 | /* flip speed goes here */ 307 | .flipper { 308 | transition: 0.6s; 309 | transform-style: preserve-3d; 310 | position: relative; 311 | } 312 | 313 | /* hide back of pane during swap */ 314 | .front, .back { 315 | -webkit-backface-visibility: hidden; 316 | backface-visibility: hidden; 317 | position: absolute; 318 | top: 0; 319 | left: 0; 320 | } 321 | 322 | /* front pane, placed above back */ 323 | .front { 324 | z-index: 2; 325 | /* for firefox 31 */ 326 | transform: rotateY(0deg); 327 | } 328 | 329 | /* back, initially hidden pane */ 330 | .back { 331 | transform: rotateY(180deg); 332 | } 333 | 334 | .card { 335 | border-radius: 15px; 336 | border-color: gray; 337 | box-shadow: 1px 1px 2px 1px #222; 338 | } 339 | 340 | .front.card { 341 | background-image: url(img/publisher.png); 342 | background-color: white; 343 | } 344 | 345 | .back.card { 346 | background-color: blue; 347 | } 348 | .purple { 349 | background-color: purple; 350 | } 351 | .purple.card { 352 | width: 64px; 353 | height: 73px; 354 | } 355 | 356 | /** */ 357 | .drag_target { 358 | outline: dotted blue; 359 | } 360 | 361 | .drag_target_hover { 362 | outline: solid blue; 363 | } 364 | 365 | /** Dice rolling effect */ 366 | 367 | /* 3D Cube */ 368 | .cube-scene { 369 | perspective: 1000px; 370 | width: 100px; 371 | height: 100px; 372 | text-align: center; 373 | display: inline-block; 374 | } 375 | 376 | .cube { 377 | display: inline-block; 378 | transition: all 0.85s cubic-bezier(0.175, 0.885, 0.32, 1.275); 379 | text-align: center; 380 | position: relative; 381 | width: 100%; 382 | height: 100%; 383 | transform-style: preserve-3d; 384 | transform: rotateX(-15deg) rotateY(15deg); 385 | top: 50%; 386 | left: 50%; 387 | transform-origin: 50%; 388 | } 389 | 390 | .cube-face { 391 | overflow: hidden; 392 | position: absolute; 393 | border: 1px solid #888; 394 | border-radius: 5px; 395 | background: #FFF; 396 | /* box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.1), 0 0 50px rgba(0, 0, 0, 0.3); */ 397 | color: #333; 398 | line-height: 100px; 399 | width: 100px; 400 | height: 100px; 401 | } 402 | 403 | .cube-face-front { 404 | transform: translate3d(0, 0, 50px); 405 | } 406 | 407 | .cube-face-top { 408 | transform: rotateX(90deg) translate3d(0, 0, 50px); 409 | } 410 | 411 | .cube-face-bottom { 412 | transform: rotateX(-90deg) translate3d(0, 0, 50px); 413 | } 414 | 415 | .cube-face-left { 416 | transform: rotateY(-90deg) translate3d(0, 0, 50px); 417 | } 418 | 419 | .cube-face-right { 420 | transform: rotateY(90deg) translate3d(0, 0, 50px); 421 | } 422 | 423 | .cube-face-back { 424 | transform: rotateY(180deg) translate3d(0, 0, 50px); 425 | } 426 | 427 | /* image sides */ 428 | .cube-face { 429 | background: url(img/64_64_dice.jpg); 430 | background-size: 600%; 431 | } 432 | 433 | .cube-face-front { 434 | background-position: 0% 0%; 435 | } 436 | 437 | .cube-face-left { 438 | background-position: 20% 0%; 439 | } 440 | 441 | .cube-face-right { 442 | background-position: 40% 0%; 443 | } 444 | 445 | .cube-face-top { 446 | background-position: 60% 0%; 447 | } 448 | 449 | .cube-face-bottom { 450 | background-position: 80% 0%; 451 | } 452 | 453 | .cube-face-back { 454 | background-position: 100% 0%; 455 | } 456 | 457 | /* controls */ 458 | 459 | 460 | 461 | #radio-back:checked ~ .cube-scene .shape { 462 | transform: rotateY(180deg) rotateX(-15deg); 463 | } 464 | 465 | #radio-left:checked ~ .cube-scene .shape { 466 | transform: rotateY(90deg); 467 | } 468 | 469 | #radio-right:checked ~ .cube-scene .shape { 470 | transform: rotateY(-90deg); 471 | } 472 | 473 | #radio-top:checked ~ .cube-scene .shape { 474 | transform: rotateX(-90deg); 475 | } 476 | 477 | #radio-bottom:checked ~ .cube-scene .shape { 478 | transform: rotateX(90deg); 479 | } 480 | 481 | /* Cilinder css */ 482 | 483 | /* /!\ You need to add vendor prefixes in order to render the CSS properly (or simply use http://leaverou.github.io/prefixfree/) /!\ */ 484 | 485 | .face { 486 | box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.4); 487 | } 488 | 489 | 490 | 491 | .scene, .shape, .face, .face-wrapper, .cr { 492 | transform-style: preserve-3d; 493 | } 494 | 495 | .face, .face-wrapper, .cr { 496 | position: absolute; 497 | } 498 | 499 | .scene { 500 | width: 80em; 501 | height: 80em; 502 | top: 50%; 503 | left: 50%; 504 | margin: -40em 0 0 -40em; 505 | perspective: 800px; 506 | } 507 | 508 | .shape { 509 | top: 50%; 510 | left: 50%; 511 | width: 0; 512 | height: 0; 513 | transform-origin: 50%; 514 | transition: all 0.85s cubic-bezier(0.175, 0.885, 0.32, 1.275); 515 | transform: rotateX(-15deg) rotateY(15deg); 516 | } 517 | .face, .face-wrapper { 518 | overflow: hidden; 519 | transform-origin: 0 0; 520 | backface-visibility: hidden; 521 | /* hidden by default, prevent blinking and other weird rendering glitchs */ 522 | } 523 | .face { 524 | background-size: 100% 100%!important; 525 | background-position: center; 526 | } 527 | .face-wrapper .face { 528 | left: 100%; 529 | width: 100%; 530 | height: 100% 531 | } 532 | .photon-shader { 533 | position: absolute; 534 | left: 0; 535 | top: 0; 536 | width: 100%; 537 | height: 100% 538 | } 539 | .side { 540 | left: 50%; 541 | } 542 | .cr, .cr .side { 543 | height: 100%; 544 | } 545 | [class*="cylinder"] .tp { 546 | transform: rotateX(90deg) translateY(-50%); 547 | } 548 | [class*="cylinder"] .bm { 549 | transform: rotateX(-90deg) translateY(-50%); 550 | } 551 | [class*="cylinder"] .tp, [class*="cylinder"] .bm, [class*="cylinder"] .tp .photon-shader, [class*="cylinder"] .bm .photon-shader { 552 | border-radius: 50%; 553 | } 554 | [class*="cylinder"] .bm { 555 | top: 100%; 556 | } 557 | /* .cyl-1 styles */ 558 | .cyl-1 { 559 | /*transform:translate3D(0em, 0.125em, 0.125em) rotateX(0deg) rotateY(0deg) rotateZ(0deg);*/ 560 | opacity:1; 561 | width:6em; 562 | height:2.75em; 563 | margin:-1.375em 0 0 -3em; 564 | } 565 | .cyl-1 .tp, .cyl-1 .bm { 566 | width:6em; 567 | height:6em; 568 | } 569 | .cyl-1 .side { 570 | width:1.3944608463408996em; 571 | height:2.75em; 572 | } 573 | .cyl-1 .s0 { 574 | transform: rotateY(12.857142857142858deg) translate3D(-50%, 0, 2.975em); 575 | } 576 | .cyl-1 .s1 { 577 | transform: rotateY(38.57142857142857deg) translate3D(-50%, 0, 2.975em); 578 | } 579 | .cyl-1 .s2 { 580 | transform: rotateY(64.28571428571429deg) translate3D(-50%, 0, 2.975em); 581 | } 582 | .cyl-1 .s3 { 583 | transform: rotateY(90deg) translate3D(-50%, 0, 2.975em); 584 | } 585 | .cyl-1 .s4 { 586 | transform: rotateY(115.71428571428572deg) translate3D(-50%, 0, 2.975em); 587 | } 588 | .cyl-1 .s5 { 589 | transform: rotateY(141.42857142857144deg) translate3D(-50%, 0, 2.975em); 590 | } 591 | .cyl-1 .s6 { 592 | transform: rotateY(167.14285714285714deg) translate3D(-50%, 0, 2.975em); 593 | } 594 | .cyl-1 .s7 { 595 | transform: rotateY(192.85714285714286deg) translate3D(-50%, 0, 2.975em); 596 | } 597 | .cyl-1 .s8 { 598 | transform: rotateY(218.57142857142858deg) translate3D(-50%, 0, 2.975em); 599 | } 600 | .cyl-1 .s9 { 601 | transform: rotateY(244.2857142857143deg) translate3D(-50%, 0, 2.975em); 602 | } 603 | .cyl-1 .s10 { 604 | transform: rotateY(270deg) translate3D(-50%, 0, 2.975em); 605 | } 606 | .cyl-1 .s11 { 607 | transform: rotateY(295.7142857142857deg) translate3D(-50%, 0, 2.975em); 608 | } 609 | .cyl-1 .s12 { 610 | transform: rotateY(321.4285714285714deg) translate3D(-50%, 0, 2.975em); 611 | } 612 | .cyl-1 .s13 { 613 | transform: rotateY(347.1428571428571deg) translate3D(-50%, 0, 2.975em); 614 | } 615 | .cyl-1 .face, .cyl-1 .side { 616 | background-color:red; 617 | } 618 | .cyl-1 .tp { 619 | background-color:red; 620 | } 621 | 622 | /* Pulse scale */ 623 | 624 | 625 | @keyframes pulsescale_anim { 626 | 0% {transform: scale(1.0);} 627 | 50% { 628 | transform: scale(1.1); 629 | } 630 | 100% {transform: scale(1.0);} 631 | } 632 | 633 | .pulsescale { 634 | animation: pulsescale_anim 2s infinite; 635 | } 636 | 637 | .scorenumber { 638 | font-size: 1em; 639 | font-weight: bold; 640 | color: maroon; 641 | text-shadow: 1px 0 1px #fff, 0 -1px 1px #fff, 0 1px 1px #fff, -1px 0 1px #fff; 642 | 643 | position: absolute; 644 | z-index: 100; 645 | transition: transform 0.5s ease-in-out; 646 | } 647 | 648 | .tmp_obj { 649 | position: absolute; 650 | z-index: 100; 651 | transition: transform 0.5s ease-in-out; 652 | } 653 | 654 | .scorenumber_anim { 655 | transform: translateY(-50px) rotate(-360deg) scale(3); 656 | transition: transform 0.5s ease-in-out; 657 | } 658 | 659 | .vapor_anim { 660 | transform: translateY(-100px) scale(1.5) ; 661 | transition: transform 2s ease-out 662 | } 663 | 664 | 665 | /** Scrollable area **/ 666 | 667 | #map_container { 668 | position: relative; 669 | width: 100%; 670 | height: 200px; 671 | overflow: hidden; 672 | 673 | border: solid yellow; 674 | margin-top: 10px; 675 | margin-left: 500px; 676 | width: 300px; 677 | } 678 | 679 | .map_scrollable_layer { 680 | position: absolute; 681 | min-height: 100px; 682 | min-width: 100px; 683 | } 684 | .map_scrollable_oversurface{ 685 | background-color: #98d7dae8; 686 | } 687 | .map_scrollable{ 688 | background-color: #525e62; 689 | } 690 | .map_surface { 691 | position: absolute; 692 | top: 0px; 693 | left: 0px; 694 | width: 100%; 695 | height: 100%; 696 | cursor: move; 697 | } 698 | .map_scrollable_oversurface{ 699 | pointer-events: none; 700 | } 701 | .map_scrollable_oversurface > *{ 702 | pointer-events: initial; 703 | } 704 | 705 | /** Move arrows **/ 706 | 707 | .movetop,.moveleft,.moveright,.movedown { 708 | display: block; 709 | position: absolute; 710 | background-image: url('../../../img/common/arrows.png'); 711 | width: 32px; 712 | height: 32px; 713 | } 714 | 715 | .movetop { 716 | top: 0px; 717 | left: calc(50% - 32px / 2); 718 | background-position: 0px 32px; 719 | } 720 | .moveleft { 721 | top: calc(50% - 32px / 2); 722 | left: 0px; 723 | background-position: 32px 0px; 724 | } 725 | .moveright { 726 | top: calc(50% - 32px / 2); 727 | right: 0px; 728 | background-position: 0px 0px; 729 | } 730 | .movedown { 731 | bottom: 0px; 732 | left: calc(50% - 32px / 2); 733 | 734 | background-position: 32px 32px; 735 | } 736 | 737 | /* flexbox*/ 738 | 739 | .card_x { 740 | background-color: white; 741 | width: 64px; 742 | height: 73px; 743 | transition: all 0.5s; 744 | } 745 | .cards-flexbox { 746 | width: 400px; 747 | height: 200px; 748 | display: flex; 749 | flex-direction: row; 750 | flex-wrap: wrap; 751 | justify-content: flex-start; 752 | align-items: flex-start; 753 | outline: green 1px solid; 754 | } 755 | .abszone { 756 | width: 400px; 757 | height: 200px; 758 | outline: red 1px solid; 759 | position: relative; 760 | padding: 10px; 761 | } 762 | .abszone > * { 763 | margin-top: 20px; 764 | } 765 | .gray { 766 | background-color: gray; 767 | } 768 | .purple { 769 | background-color: purple; 770 | } 771 | .red { 772 | background-color: red; 773 | } 774 | .blue { 775 | background-color: blue; 776 | } -------------------------------------------------------------------------------- /sharedcode.game.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 5 | * SharedCode implementation : © Alena Laskavaia 6 | * 7 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 8 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 9 | * ----- 10 | * 11 | * sharedcode.game.php 12 | * 13 | * This is the main file for your game logic. 14 | * 15 | * In this PHP file, you are going to defines the rules of the game. 16 | * 17 | */ 18 | require_once (APP_GAMEMODULE_PATH . 'module/table/table.game.php'); 19 | require_once (APP_GAMEMODULE_PATH . 'module/common/deck.game.php'); 20 | 21 | 22 | require_once ('modules/EuroGame.php'); 23 | 24 | class SharedCode extends EuroGame { 25 | 26 | function __construct() { 27 | // Your global variables labels: 28 | // Here, you can assign labels to global variables you are using for this game. 29 | // You can use any number of global variables with IDs between 10 and 99. 30 | // If your game has options (variants), you also have to associate here a label to 31 | // the corresponding ID in gameoptions.inc.php. 32 | // Note: afterwards, you can get/set the global variables with getGameStateValue/setGameStateInitialValue/setGameStateValue 33 | parent::__construct(); 34 | self::initGameStateLabels(array ( 35 | // reserved globals 1..10 36 | 'move_nbr' => 6, 37 | // game global vars starts at 10 38 | "round" => 10, 39 | "resource_id_counter" => 11, 40 | 41 | // ... 42 | // "my_first_game_variant" => 100, 43 | // "my_second_game_variant" => 101, 44 | // ... 45 | )); 46 | $this->cards = self::getNew( "module.common.deck" ); 47 | $this->cards->init( "card" ); 48 | 49 | } 50 | 51 | protected function getGameName() { 52 | // Used for translations and stuff. Please do not modify. 53 | return "sharedcode"; 54 | } 55 | 56 | /* 57 | * setupNewGame: 58 | * 59 | * This method is called only once, when a new game is launched. 60 | * In this method, you must setup the game according to the game rules, so that 61 | * the game is ready to be played. 62 | */ 63 | protected function setupNewGame($players, $options = array ()) { 64 | /** 65 | * ********** Start the game initialization **** 66 | */ 67 | $this->initPlayers($players); 68 | $this->initStats(); 69 | // Setup the initial game situation here 70 | $this->initTables(); 71 | /** 72 | * ********** End of the game initialization **** 73 | */ 74 | } 75 | 76 | protected function initTables() { 77 | 78 | // Create cards 79 | $cards = array(); 80 | 81 | $cards[] = array( 'type' => 'card_red', 'type_arg' => 1, 'nbr' => 10); 82 | $cards[] = array( 'type' => 'card_blue', 'type_arg' => 2, 'nbr' => 8); 83 | $cards[] = array( 'type' => 'card_green', 'type_arg' => 3, 'nbr' => 5); 84 | 85 | 86 | $this->cards->createCards( $cards, 'deck' ); 87 | $this->cards->shuffle('deck'); 88 | $players = self::loadPlayersBasicInfos(); 89 | foreach ( $players as $player_id => $player ) { 90 | $this->cards->pickCards(3, 'deck', $player_id); 91 | } 92 | 93 | // Init global values with their initial values 94 | self::setGameStateInitialValue('resource_id_counter', 1); 95 | self::setGameStateInitialValue('round', 0); 96 | } 97 | 98 | /* 99 | * getAllDatas: 100 | * 101 | * Gather all informations about current game situation (visible by the current player). 102 | * 103 | * The method is called each time the game interface is displayed to a player, ie: 104 | * _ when the game starts 105 | * _ when a player refreshes the game page (F5) 106 | */ 107 | protected function getAllDatas() { 108 | $result = parent::getAllDatas(); 109 | $current_player_id = self::getCurrentPlayerId(); 110 | $cards = $this->cards->getCardsInLocation( 'hand', $current_player_id+0, "type_arg" ); 111 | 112 | $result['hand']=$cards; 113 | $result['playarea']= $this->cards->getCardsInLocation( 'playarea', null, "location_arg" ); 114 | return $result; 115 | } 116 | 117 | /* 118 | * getGameProgression: 119 | * 120 | * Compute and return the current game progression. 121 | * The number returned must be an integer beween 0 (=the game just started) and 122 | * 100 (= the game is finished or almost finished). 123 | * 124 | * This method is called each time we are in a game state with the "updateGameProgression" property set to true 125 | * (see states.inc.php) 126 | */ 127 | function getGameProgression() { 128 | $round = $this->getGameStateValue('round'); 129 | if ($round>=6) return 100; 130 | return 100*$round/6; 131 | } 132 | 133 | ////////////////////////////////////////////////////////////////////////////// 134 | //////////// Utility functions 135 | //////////// 136 | /* 137 | * In this space, you can put any utility methods useful for your game logic 138 | */ 139 | 140 | 141 | ////////////////////////////////////////////////////////////////////////////// 142 | //////////// Player actions 143 | //////////// 144 | /* 145 | * Each time a player is doing some game action, one of the methods below is called. 146 | * (note: each method below must match an input method in sharedcode.action.php) 147 | */ 148 | 149 | function action_pass(){ 150 | self::notifyWithName( "message", clienttranslate( '${player_name} is passed' )); 151 | $this->gamestate->nextState('pass'); 152 | } 153 | 154 | function action_selectWorkerAction($action_id, $worker_id) { 155 | self::checkAction('selectWorkerAction'); 156 | switch ($action_id) { 157 | case 'action_space_1': 158 | $this->gamestate->nextState('playCubes'); 159 | return; 160 | case 'action_space_2': 161 | $this->gamestate->nextState('playCards'); 162 | return; 163 | default: 164 | $this->userAssertTrue(self::_("Action is not supported yet"),false,$action_id); 165 | return; 166 | } 167 | } 168 | 169 | function action_takeCube($token_id, $place_id) { 170 | self::checkAction('takeCube'); 171 | $this->dbSetTokenLocation($token_id, $place_id, 0); 172 | $this->gamestate->nextState('next'); 173 | } 174 | 175 | function action_moveCube($token_id, $place_id) { 176 | self::checkAction('moveCube'); 177 | $this->dbSetTokenLocation($token_id, $place_id, 0); 178 | $this->gamestate->nextState('next'); 179 | } 180 | 181 | function action_drawCard() { 182 | self::checkAction('drawCard'); 183 | $player_id = self::getActivePlayerId(); 184 | $cards = $this->cards->pickCards(1, 'deck', $player_id); 185 | $this->notifyPlayer($player_id, 'moveCard', '${You} draw a card', [ 'You' => 'You', 'card' => $cards[0] ]); 186 | $this->gamestate->nextState('next'); 187 | } 188 | 189 | function action_playCard($card_id) { 190 | self::checkAction('playCard'); 191 | $player_id = self::getActivePlayerId(); 192 | $this->cards->moveCard($card_id,'playarea'); 193 | $this->notifyWithName('moveCard', '${player_name} draw a card', [ 'card' => $this->cards->getCard($card_id) ], $player_id); 194 | $this->gamestate->nextState('next'); 195 | } 196 | 197 | function stMultiactive() { 198 | $this->gamestate->setAllPlayersMultiactive(); 199 | } 200 | 201 | ////////////////////////////////////////////////////////////////////////////// 202 | //////////// Game state arguments 203 | //////////// 204 | /* 205 | * Here, you can create methods defined as "game state arguments" (see "args" property in states.inc.php). 206 | * These methods function is to return some additional information that is specific to the current 207 | * game state. 208 | */ 209 | function arg_playerTurn() { 210 | $round = self::getGameStateValue('round'); 211 | return array ('round' => $round ); 212 | } 213 | 214 | function arg_playerTurnPlayCubes() { 215 | // Get some values from the current game situation in database... 216 | $takeCubeNumber = bga_rand(0, 10); 217 | $counter = self::getGameStateValue('resource_id_counter'); 218 | // return values: 219 | return array ('cubeTypeNumber' => $takeCubeNumber,'resource_id_counter' => $counter ); 220 | } 221 | function arg_playerTurnPlayCards() { 222 | return []; 223 | } 224 | ////////////////////////////////////////////////////////////////////////////// 225 | //////////// Game state actions 226 | //////////// 227 | /** 228 | * Typical function that changes to next player 229 | */ 230 | function st_gameTurn() { 231 | $this->activeNextPlayer(); 232 | $player_id = $this->getActivePlayerId(); 233 | $this->giveExtraTime($player_id); 234 | $first_player_id = $this->getFirstPlayer(); 235 | if ($player_id == $first_player_id) { 236 | $round = $this->incGameStateValue('round', 1); 237 | if ($round >= 6) { 238 | $this->gamestate->nextState('endGame'); 239 | return; 240 | } 241 | $this->incStat(1, 'turns_number'); 242 | } 243 | $this->incStat(1, 'turns_number', $player_id); 244 | $this->gamestate->nextState('next'); 245 | } 246 | ////////////////////////////////////////////////////////////////////////////// 247 | //////////// Zombie 248 | //////////// 249 | /* 250 | * zombieTurn: 251 | * 252 | * This method is called each time it is the turn of a player who has quit the game (= "zombie" player). 253 | * You can do whatever you want in order to make sure the turn of this player ends appropriately 254 | * (ex: pass). 255 | */ 256 | function zombieTurn($state, $active_player) { 257 | $statename = $state ['name']; 258 | if ($state ['type'] == "activeplayer") { 259 | switch ($statename) { 260 | default : 261 | $this->gamestate->nextState("zombiePass"); 262 | break; 263 | } 264 | return; 265 | } 266 | if ($state ['type'] == "multipleactiveplayer") { 267 | // Make sure player is in a non blocking status for role turn 268 | $sql = " 269 | UPDATE player 270 | SET player_is_multiactive = 0 271 | WHERE player_id = $active_player 272 | "; 273 | self::DbQuery($sql); 274 | $this->gamestate->updateMultiactiveOrNextState(''); 275 | return; 276 | } 277 | throw new feException("Zombie mode not supported at this game state: " . $statename); 278 | } 279 | ///////////////////////////////////////////////////////////////////////////////////: 280 | ////////// DB upgrade 281 | ////////// 282 | /* 283 | * upgradeTableDb: 284 | * 285 | * You don't have to care about this until your game has been published on BGA. 286 | * Once your game is on BGA, this method is called everytime the system detects a game running with your old 287 | * Database scheme. 288 | * In this case, if you change your Database scheme, you just have to apply the needed changes in order to 289 | * update the game database and allow the game to continue to run with your new version. 290 | * 291 | */ 292 | function upgradeTableDb($from_version) { 293 | // $from_version is the current version of this game database, in numerical form. 294 | // For example, if the game was running with a release of your game named "140430-1345", 295 | // $from_version is equal to 1404301345 296 | // Example: 297 | // if( $from_version <= 1404301345 ) 298 | // { 299 | // $sql = "ALTER TABLE xxxxxxx ...."; 300 | // self::DbQuery( $sql ); 301 | // } 302 | // if( $from_version <= 1405061421 ) 303 | // { 304 | // $sql = "CREATE TABLE xxxxxxx ...."; 305 | // self::DbQuery( $sql ); 306 | // } 307 | // // Please add your future database scheme changes here 308 | // 309 | // 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /sharedcode.view.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 5 | * SharedCode implementation : © Alena Laskavaia 6 | * 7 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 8 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 9 | * ----- 10 | * 11 | * sharedcode.view.php 12 | * 13 | * This is your "view" file. 14 | * 15 | * The method "build_page" below is called each time the game interface is displayed to a player, ie: 16 | * _ when the game starts 17 | * _ when a player refreshes the game page (F5) 18 | * 19 | * "build_page" method allows you to dynamically modify the HTML generated for the game interface. In 20 | * particular, you can set here the values of variables elements defined in sharedcode_sharedcode.tpl (elements 21 | * like {MY_VARIABLE_ELEMENT}), and insert HTML block elements (also defined in your HTML template file) 22 | * 23 | * Note: if the HTML of your game interface is always the same, you don't have to place anything here. 24 | * 25 | */ 26 | 27 | require_once( APP_BASE_PATH."view/common/game.view.php" ); 28 | 29 | class view_sharedcode_sharedcode extends game_view 30 | { 31 | function getGameName() { 32 | return "sharedcode"; 33 | } 34 | function build_page( $viewArgs ) 35 | { 36 | // Get players & players number 37 | $players = $this->game->loadPlayersBasicInfos(); 38 | $players_nbr = count( $players ); 39 | 40 | /*********** Place your code below: ************/ 41 | 42 | 43 | /* 44 | 45 | // Examples: set the value of some element defined in your tpl file like this: {MY_VARIABLE_ELEMENT} 46 | 47 | // Display a specific number / string 48 | $this->tpl['MY_VARIABLE_ELEMENT'] = $number_to_display; 49 | 50 | // Display a string to be translated in all languages: 51 | $this->tpl['MY_VARIABLE_ELEMENT'] = self::_("A string to be translated"); 52 | 53 | // Display some HTML content of your own: 54 | $this->tpl['MY_VARIABLE_ELEMENT'] = self::raw( $some_html_code ); 55 | 56 | */ 57 | 58 | /* 59 | 60 | // Example: display a specific HTML block for each player in this game. 61 | // (note: the block is defined in your .tpl file like this: 62 | // 63 | // ... my HTML code ... 64 | // 65 | 66 | 67 | $this->page->begin_block( "sharedcode_sharedcode", "myblock" ); 68 | foreach( $players as $player ) 69 | { 70 | $this->page->insert_block( "myblock", array( 71 | "PLAYER_NAME" => $player['player_name'], 72 | "SOME_VARIABLE" => $some_value 73 | ... 74 | ) ); 75 | } 76 | 77 | */ 78 | 79 | 80 | 81 | /*********** Do not change anything below this line ************/ 82 | } 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /sharedcode_sharedcode.tpl: -------------------------------------------------------------------------------- 1 | {OVERALL_GAME_HEADER} 2 | 3 | 27 | 28 |
29 |
30 | This is fake game board. 31 |
32 | Moving tokens and counters 33 |
34 |
35 | Playing with cards 36 |
37 |
38 | Something else 39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 |

My Hand (Stock and DragNDrop)

48 |
49 |
50 | 51 | 52 | 53 | 54 | refresh 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 |
69 |
70 |

Play Area (Stock)

71 |
72 |
73 |
74 |
75 |

Various BGA Framework dialogs

76 | askForValueDialog 77 | edit value 78 | multipleChoiceDialog 79 |
80 |
81 |

Flip Cards and Roll Dice animations

82 |
83 |
84 |
85 | 86 |
87 |
88 | 89 |
90 |
91 |
92 | 93 |
94 | 95 | 96 | 97 | 98 | 99 |
Click on radio buttons
100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |

Scroll Map Area

165 |
166 |
167 |
168 |
169 | 170 |
171 |
172 |
173 |
174 |
175 |
176 | 177 |
178 |

Flexbox Animation (No stock)

179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |

Absolute positioning zone

188 |
189 |
190 |
191 |
192 |
193 |
194 | 203 | 204 | {OVERALL_GAME_FOOTER} 205 | -------------------------------------------------------------------------------- /states.inc.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 5 | * SharedCode implementation : © Alena Laskavaia 6 | * 7 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 8 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 9 | * ----- 10 | * 11 | * states.inc.php 12 | * 13 | * SharedCode game states description 14 | * 15 | */ 16 | 17 | /* 18 | Game state machine is a tool used to facilitate game developpement by doing common stuff that can be set up 19 | in a very easy way from this configuration file. 20 | 21 | Please check the BGA Studio presentation about game state to understand this, and associated documentation. 22 | 23 | Summary: 24 | 25 | States types: 26 | _ activeplayer: in this type of state, we expect some action from the active player. 27 | _ multipleactiveplayer: in this type of state, we expect some action from multiple players (the active players) 28 | _ game: this is an intermediary state where we don't expect any actions from players. Your game logic must decide what is the next game state. 29 | _ manager: special type for initial and final state 30 | 31 | Arguments of game states: 32 | _ name: the name of the GameState, in order you can recognize it on your own code. 33 | _ description: the description of the current game state is always displayed in the action status bar on 34 | the top of the game. Most of the time this is useless for game state with "game" type. 35 | _ descriptionmyturn: the description of the current game state when it's your turn. 36 | _ type: defines the type of game states (activeplayer / multipleactiveplayer / game / manager) 37 | _ action: name of the method to call when this game state become the current game state. Usually, the 38 | action method is prefixed by "st" (ex: "stMyGameStateName"). 39 | _ possibleactions: array that specify possible player actions on this step. It allows you to use "checkAction" 40 | method on both client side (Javacript: this.checkAction) and server side (PHP: self::checkAction). 41 | _ transitions: the transitions are the possible paths to go from a game state to another. You must name 42 | transitions in order to use transition names in "nextState" PHP method, and use IDs to 43 | specify the next game state for each transition. 44 | _ args: name of the method to call to retrieve arguments for this gamestate. Arguments are sent to the 45 | client side to be used on "onEnteringState" or to set arguments in the gamestate description. 46 | _ updateGameProgression: when specified, the game progression is updated (=> call to your getGameProgression 47 | method). 48 | */ 49 | 50 | // !! It is not a good idea to modify this file when a game is running !! 51 | 52 | // define contants for state ids 53 | if (!defined('STATE_END_GAME')) { // guard since this included multiple times 54 | define("STATE_PLAYER_TURN", 2); 55 | define("STATE_GAME_TURN", 3); 56 | define("STATE_PLAYER_TURN_CUBES", 4); 57 | define("STATE_PLAYER_TURN_CARDS", 5); 58 | define("STATE_END_GAME", 99); 59 | } 60 | 61 | $machinestates = array( 62 | 63 | // The initial state. Please do not modify. 64 | 1 => array( 65 | "name" => "gameSetup", 66 | "description" => "", 67 | "type" => "manager", 68 | "action" => "stGameSetup", 69 | "transitions" => array( "" => STATE_PLAYER_TURN ) 70 | ), 71 | 72 | // Note: ID=2 => your first state 73 | 74 | STATE_PLAYER_TURN => array( 75 | "name" => "playerTurn", 76 | "description" => clienttranslate('${actplayer} must select an Action Space or Pass'), 77 | "descriptionmyturn" => clienttranslate('${you} must select an Action Space or Pass'), 78 | "type" => "activeplayer", 79 | 'args' => 'arg_playerTurn', 80 | "possibleactions" => array( "selectWorkerAction", "pass" ), 81 | "transitions" => array( 82 | "loopback" => STATE_PLAYER_TURN, 83 | "playCubes" => STATE_PLAYER_TURN_CUBES, 84 | "playCards" => STATE_PLAYER_TURN_CARDS, 85 | "pass" => STATE_GAME_TURN ) 86 | ), 87 | 88 | STATE_GAME_TURN => array( 89 | "name" => "gameTurn", 90 | "description" => clienttranslate('Switching to next player'), 91 | "type" => "game", 92 | "action" => "st_gameTurn", 93 | "updateGameProgression" => true, 94 | "transitions" => array( 95 | "next" => STATE_PLAYER_TURN, 96 | "endGame" => STATE_END_GAME ) 97 | ), 98 | STATE_PLAYER_TURN_CUBES => array( 99 | "name" => "playerTurnPlayCubes", 100 | "description" => clienttranslate('${actplayer} must select Take a Cube or Move a Cube'), 101 | "descriptionmyturn" => clienttranslate('${you} must select Take a Cube or Move a Cube'), 102 | "type" => "activeplayer", 103 | 'args' => 'arg_playerTurnPlayCubes', 104 | "possibleactions" => array( "takeCube", "moveCube" ), 105 | "transitions" => array( 106 | "loopback" => STATE_PLAYER_TURN_CUBES, 107 | "next" => STATE_GAME_TURN ) 108 | ), 109 | STATE_PLAYER_TURN_CARDS => array( 110 | "name" => "playerTurnPlayCards", 111 | "description" => clienttranslate('${actplayer} must select to draw card or play card'), 112 | "descriptionmyturn" => clienttranslate('${you} must select to draw card or play card'), 113 | "type" => "activeplayer", 114 | 'args' => 'arg_playerTurnPlayCards', 115 | "possibleactions" => array( "drawCard", "playCard" ), 116 | "transitions" => array( 117 | "loopback" => STATE_PLAYER_TURN_CARDS, 118 | "next" => STATE_GAME_TURN ) 119 | ), 120 | 121 | 122 | // Final state. 123 | // Please do not modify. 124 | STATE_END_GAME => array( 125 | "name" => "gameEnd", 126 | "description" => clienttranslate("End of game"), 127 | "type" => "manager", 128 | "action" => "stGameEnd", 129 | "args" => "argGameEnd" 130 | ) 131 | 132 | ); 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /stats.inc.php: -------------------------------------------------------------------------------- 1 | & Emmanuel Colin 6 | * SharedCode implementation : © Alena Laskavaia 7 | * 8 | * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. 9 | * See http://en.boardgamearena.com/#!doc/Studio for more information. 10 | * ----- 11 | * 12 | * stats.inc.php 13 | * 14 | * SharedCode game statistics description 15 | * 16 | */ 17 | 18 | /* 19 | In this file, you are describing game statistics, that will be displayed at the end of the 20 | game. 21 | 22 | !! After modifying this file, you must use "Reload statistics configuration" in BGA Studio backoffice 23 | ("Control Panel" / "Manage Game" / "Your Game") 24 | 25 | There are 2 types of statistics: 26 | _ table statistics, that are not associated to a specific player (ie: 1 value for each game). 27 | _ player statistics, that are associated to each players (ie: 1 value for each player in the game). 28 | 29 | Statistics types can be "int" for integer, "float" for floating point values, and "bool" for boolean 30 | 31 | Once you defined your statistics there, you can start using "initStat", "setStat" and "incStat" method 32 | in your game logic, using statistics names defined below. 33 | 34 | !! It is not a good idea to modify this file when a game is running !! 35 | 36 | If your game is already public on BGA, please read the following before any change: 37 | http://en.doc.boardgamearena.com/Post-release_phase#Changes_that_breaks_the_games_in_progress 38 | 39 | Notes: 40 | * Statistic index is the reference used in setStat/incStat/initStat PHP method 41 | * Statistic index must contains alphanumerical characters and no space. Example: 'turn_played' 42 | * Statistics IDs must be >=10 43 | * Two table statistics can't share the same ID, two player statistics can't share the same ID 44 | * A table statistic can have the same ID than a player statistics 45 | * Statistics ID is the reference used by BGA website. If you change the ID, you lost all historical statistic data. Do NOT re-use an ID of a deleted statistic 46 | * Statistic name is the English description of the statistic as shown to players 47 | 48 | */ 49 | 50 | $stats_type = array( 51 | 52 | // Statistics global to table 53 | "table" => array( 54 | 55 | "turns_number" => array("id"=> 10, 56 | "name" => totranslate("Number of turns"), 57 | "type" => "int" ), 58 | 59 | /* 60 | Examples: 61 | 62 | 63 | "table_teststat1" => array( "id"=> 10, 64 | "name" => totranslate("table test stat 1"), 65 | "type" => "int" ), 66 | 67 | "table_teststat2" => array( "id"=> 11, 68 | "name" => totranslate("table test stat 2"), 69 | "type" => "float" ) 70 | */ 71 | ), 72 | 73 | // Statistics existing for each player 74 | "player" => array( 75 | 76 | "turns_number" => array("id"=> 10, 77 | "name" => totranslate("Number of turns"), 78 | "type" => "int" ), 79 | 80 | /* 81 | Examples: 82 | 83 | 84 | "player_teststat1" => array( "id"=> 10, 85 | "name" => totranslate("player test stat 1"), 86 | "type" => "int" ), 87 | 88 | "player_teststat2" => array( "id"=> 11, 89 | "name" => totranslate("player test stat 2"), 90 | "type" => "float" ) 91 | 92 | */ 93 | ) 94 | 95 | ); 96 | -------------------------------------------------------------------------------- /userscripts/bgabugredirect.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name BGA bug report view 3 | // @namespace https://boardgamearena.com/ 4 | // @version 0.1 5 | // @description makes bug link open new window 6 | // @author elaskavaia 7 | // @match https://boardgamearena.com/bugs?* 8 | // @icon https://www.google.com/s2/favicons?sz=64&domain=boardgamearena.com 9 | // @run-at document-idle 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | function updateBugs() { 14 | console.log("BGA bug report view"); 15 | // Your code here... 16 | const targetNode = document.querySelector("#buglist_inner"); 17 | 18 | // Callback function to execute when mutations are observed 19 | const callback = (mutationList, _observer) => { 20 | for (const mutation of mutationList) { 21 | if (mutation.type === "childList") { 22 | console.log("A child node has been added or removed.", mutation); 23 | blanket(mutation.target); 24 | } 25 | } 26 | }; 27 | 28 | // Create an observer instance linked to the callback function 29 | const observer = new MutationObserver(callback); 30 | 31 | // Start observing the target node for configured mutations 32 | observer.observe(targetNode, { childList: true }); 33 | 34 | blanket(document); 35 | 36 | // Later, you can stop observing 37 | //observer.disconnect(); 38 | } 39 | 40 | function blanket(parent) { 41 | parent.querySelectorAll(".bugrow").forEach((tr) => { 42 | tr.querySelectorAll("a").forEach((node) => { 43 | if (node.target != "_blank") { 44 | node.target = "_blank"; // bugs should open in new window 45 | //console.log("fixing", node.href); 46 | //node.innerHTML += " ^"; 47 | } 48 | }); 49 | tr.style.backgroundColor = "gray"; 50 | }); 51 | } 52 | 53 | (function () { 54 | "use strict"; 55 | 56 | setTimeout(updateBugs, 500); 57 | })(); 58 | -------------------------------------------------------------------------------- /userscripts/bgalicensesorter.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name BGA Licenses Table Sorter 3 | // @description adds sorting fields to license table 4 | // @author elaskavaia 5 | // @match *://studio.boardgamearena.com/licensing 6 | // @icon https://www.google.com/s2/favicons?sz=64&domain=boardgamearena.com 7 | // @downloadURL https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/master/userscripts/bgalicensesorter.user.js 8 | // @updateURL https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/master/userscripts/bgalicensesorter.user.js 9 | // @run-at document-idle 10 | // @version 0.4 11 | // @grant none 12 | 13 | // ==/UserScript== 14 | 15 | (function () { 16 | "use strict"; 17 | console.log("BGA sorter is loaded"); 18 | 19 | function sortByColumn(column) { 20 | function compareFuncComplexity(a, b) { 21 | const avalue = a.children[column].textContent.trim(); 22 | const bvalue = b.children[column].textContent.trim(); 23 | try { 24 | return parseFloat(avalue) > parseFloat(bvalue) ? -1 : 1; 25 | } catch (e) { 26 | return avalue > bvalue ? -1 : 1; 27 | } 28 | } 29 | 30 | var container = document.querySelector(".statstable tbody"); 31 | var children = Array.prototype.slice.call(container.children); 32 | 33 | var sortedChildren = children.sort(compareFuncComplexity); 34 | 35 | sortedChildren.forEach((element) => { 36 | container.appendChild(element); 37 | }); 38 | } 39 | 40 | function addButton(name, column) { 41 | let selector = document.getElementById("sort_selector"); 42 | let div = document.createElement("div"); 43 | div.innerHTML = ` 44 | 45 | 46 | `; 47 | selector.appendChild(div); 48 | let button = selector.querySelector(`#licenses_by_${name}`); 49 | button.addEventListener("click", () => setTimeout(() => sortByColumn(column), 1)); 50 | } 51 | 52 | addButton("bggrating", 5); 53 | addButton("complexity", 6); 54 | })(); 55 | -------------------------------------------------------------------------------- /userscripts/bugcannedanswers.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name BGA Bug report canned replies 3 | // @namespace https://boardgamearena.com/ 4 | // @version 0.2 5 | // @description select canned reply 6 | // @author elaskavaia 7 | // @match https://boardgamearena.com/bug?id=* 8 | // @icon https://www.google.com/s2/favicons?sz=64&domain=boardgamearena.com 9 | // @downloadURL https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/master/userscripts/bugcannedanswers.user.js 10 | // @updateURL https://raw.githubusercontent.com/elaskavaia/bga-sharedcode/master/userscripts/bugcannedanswers.user.js 11 | // @run-at document-idle 12 | // @grant none 13 | // ==/UserScript== 14 | 15 | (function () { 16 | "use strict"; 17 | 18 | // Your code here... 19 | //debugger; 20 | let table = [ 21 | { 22 | title: "Rule X", 23 | text: "Not a bug. Please conslult the rule book.", 24 | resolution: "notabug", 25 | acton: "change_bug_status" 26 | }, 27 | { 28 | title: "Missing screenshot", 29 | text: "Insufficient information. Please provide table number, move number and screenshot", 30 | resolution: "infoneeded", 31 | acton: "change_bug_status" 32 | }, 33 | { 34 | title: "Missing move", 35 | text: "Insufficient information. Please provide table number, move number and/or log details", 36 | resolution: "infoneeded", 37 | acton: "change_bug_status" 38 | } 39 | ]; 40 | let tools = document.querySelector("#moderator_quicktools .pagesection__content"); 41 | tools.innerHTML += "

Canned answers:

"; 42 | const report_log = document.getElementById("report_log"); 43 | for (let i in table) { 44 | const id = `${table[i].acton}_${table[i].resolution}`; 45 | const a = document.createElement("a"); 46 | a.id = id; 47 | a.classList.add("bgabutton", "bgabutton_blue", table[i].acton); 48 | a.innerHTML = table[i].title; 49 | tools.appendChild(a); 50 | tools.appendChild(document.createTextNode(" ")); 51 | const text = table[i].text; 52 | 53 | a.addEventListener("click", (event) => { 54 | report_log.innerHTML = text; 55 | }); 56 | } 57 | })(); 58 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 |