<?php
require_once( APP_GAMEMODULE_PATH.'module/table/table.game.php' );
require_once( 'modules/tokens.php' );
require_once( 'modules/utils.php' );
class pi extends Table
{
function __construct( )
{
parent::__construct();
self::initGameStateLabels(array(
"minigame" => 10,
"minigame_round" => 11,
"points_winnable" => 12,
));
$this->cards = self::getNew("module.common.deck");
$this->cards->init("card");
$this->tokens = new Tokens();
}
protected function getGameName( )
{
return "pi";
}
protected function setupNewGame($players, $options=array())
{
$gameinfos = self::getGameinfos();
$default_colors = $gameinfos['player_colors'];
$sql = "INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar) VALUES ";
$values = array();
foreach($players as $player_id => $player)
{
$color = array_shift($default_colors);
$values[] = "('".$player_id."','$color','".$player['player_canal']."','".addslashes( $player['player_name'] )."','".addslashes( $player['player_avatar'] )."')";
}
$sql .= implode( $values, ',' );
self::DbQuery($sql);
self::reattributeColorsBasedOnPreferences( $players, $gameinfos['player_colors'] );
self::reloadPlayersBasicInfos();
self::DbQuery("UPDATE player SET player_score = 0, player_score_aux = 500");
$cards = array();
foreach ($this->cardBasis as $card_id => $card) {
$cards[] = array(
'type' => ($card_id <= 36) ? 'evidence' : $card['casetype'],
'type_arg' => $card_id,
'nbr' => 1);
}
foreach ($this->tiles as $tile_id => $tile) {
$cards[] = array(
'type' => 'tile_' . $tile['tiletype'],
'type_arg' => $tile_id,
'nbr' => 1);
}
$this->cards->createCards($cards, 'offtable');
$players = self::loadPlayersBasicInfos();
foreach($players as $player_id => $player) {
$color = $this->constants['HEX2COLORNAME'][$player['player_color']];
$player_tokens = array_filter($this->tokeninfos, function ($v) use ($color) {
return strpos($v['key'], "_{$color}") > 0;
});
$this->tokens->createTokens($player_tokens, 'supply');
$this->tokens->moveTokens(
array_pluck($this->tokens->getTokensOfTypeInLocation("pi_{$color}_%"), 'key'),
"pi_supply_{$player_id}");
$this->tokens->moveToken("penalty_{$color}", "penalty_0");
}
self::initStat('table', 'turns_number', 0);
self::initStat('table', 'rounds_1', 0);
self::initStat('table', 'rounds_2', 0);
self::initStat('table', 'rounds_3', 0);
self::initStat('player', 'turns_number', 0);
self::initStat('player', 'cards_taken_1', 0);
self::initStat('player', 'cards_taken_2', 0);
self::initStat('player', 'cards_taken_3', 0);
self::initStat('player', 'investigators_used_1', 0);
self::initStat('player', 'investigators_used_2', 0);
self::initStat('player', 'investigators_used_3', 0);
self::initStat('player', 'penalty_1', 0);
self::initStat('player', 'penalty_2', 0);
self::initStat('player', 'penalty_3', 0);
self::initStat('player', 'vp_1', 0);
self::initStat('player', 'vp_2', 0);
self::initStat('player', 'vp_3', 0);
self::initStat('player', 'solved_minigames', 0);
self::initStat('player', 'avg_investigator_neighborhood', $this->constants['AVG_LOCATION_NEIGHBORS']);
self::initStat('player', 'neighbor_case_cards_taken', 0);
self::initStat('player', 'avg_cubes_to_solve', $this->constants['CUBES_PER_PLAYER']);
self::initStat('player', 'avg_discs_to_solve', $this->constants['DISCS_PER_PLAYER']);
self::setGameStateInitialValue('minigame', 0); self::setGameStateInitialValue('minigame_round', 0); self::setGameStateInitialValue('points_winnable', 7);
}
protected function getAllDatas()
{
$result = array();
$result['cardinfos'] = $this->cardBasis;
$result['locationinfos'] = $this->locations;
$result['tileinfos'] = $this->tiles;
$result = array_merge($result, $this->getPrivateGameInfos(self::getCurrentPlayerId()));
$result = array_merge($result, $this->getPublicGameInfos());
return $result;
}
function getGameProgression()
{
$base = (self::getGameStateValue("minigame") - 1) * (100 / $this->constants['MINIGAMES']);
$base = max(0, $base);
$discs_on_agentarea = count($this->tokens->getTokensOfTypeInLocation('disc_%', 'agentarea_%'));
$discs_on_locslot = count($this->tokens->getTokensOfTypeInLocation('disc_%', 'locslot_%'));
$perc_cases_solved = 0;
$perc_cases_solved += $discs_on_agentarea * (1/9);
$perc_cases_solved += $discs_on_locslot * (1/3);
$minigame_progress = $perc_cases_solved / self::getPlayersNumber();
$progress = $base + ($minigame_progress * 33);
return floor($progress);
}
function getCorrespondingTile($card_id)
{
$card = $this->cards->getCard($card_id);
$cardinfo = $this->cardBasis[$card['type_arg']];
$tile_mid = null;
foreach ($this->tiles as $mid => $tile) {
if ($tile['name'] == $cardinfo['name']) {
$tile_mid = $mid;
break;
}
}
$tiles = $this->cards->getCardsOfType("tile_{$cardinfo['casetype']}", $tile_mid);
return array_shift($tiles);
}
function getLocationIdOfTile($tile)
{
if ($tile['location'] != 'locslot') {
throw new BgaVisibleSystemException("Tile is not on the board. Please report this.");
}
$tile_slot_id = $tile['location_arg'];
return floor((int)$tile_slot_id / 100); }
function getAdjacentTileNames($location_id, $tile_type=null)
{
$neighbor_mids = $this->locations[$location_id]['neighbors'];
$adjacent_slot_ids = array_pluck(
array_flatten(array_pluck(
array_filter_by_keys($this->locations, $neighbor_mids),
'slots')),
'id');
$sql = array();
$sql[] = "SELECT card_type_arg FROM `card` WHERE 1";
$sql[] = "AND card_location_arg IN (" . implode(',', $adjacent_slot_ids) . ")";
if ($tile_type) {
$sql[] = "AND card_type = '{$tile_type}'";
}
$sql = implode(' ', $sql);
$tile_mids = self::getObjectListFromDB($sql, true);
return array_pluck(array_filter_by_keys($this->tiles, $tile_mids), 'name');
}
function getAdjacentTileNamesFromCard($card_id)
{
$tile = $this->getCorrespondingTile($card_id);
$location_mid = $this->getLocationIdOfTile($tile);
return $this->getAdjacentTileNames($location_mid, $tile['type']);
}
function getPlayerCaseSolution($player_id)
{
$card_mids = array_pluck($this->getPlayerCaseCards($player_id), 'type_arg');
$solution = array(
$this->cardBasis[$card_mids[0]]['casetype'] => $this->cardBasis[$card_mids[0]]['name'],
$this->cardBasis[$card_mids[1]]['casetype'] => $this->cardBasis[$card_mids[1]]['name'],
$this->cardBasis[$card_mids[2]]['casetype'] => $this->cardBasis[$card_mids[2]]['name']
);
return $solution;
}
function getPrivateGameInfos($player_id)
{
return array(
'hand' => $this->cards->getCardsInLocation('hand', $player_id)
);
}
function getPublicGameInfos()
{
$minigame = self::getGameStateValue("minigame");
$counters = array();
$this->setCounter($counters, "current_minigame", $minigame);
$start_player_no = $this->getStartPlayerNo($minigame);
$sql = "
SELECT
player_id as id,
player_color as color,
player_score as score,
player_solved_in_round as solved_in_round,
player_no = {$start_player_no} as is_startplayer
FROM player
";
$players = self::getCollectionFromDb($sql);
foreach ($players as $idx => $player) {
$players[$idx]['colorname'] = $this->constants['HEX2COLORNAME'][$player['color']];
$this->setCounter(
$counters, "remaining_investigators_" . $player['id'],
$this->tokens->countTokensInLocation("pi_supply_" . $player['id']));
}
return array(
'counters' => $counters,
'players' => $players,
'evidence_display' => $this->cards->getCardsInLocation('evidence_display'),
'evidence_discard' => $this->cards->getCardsInLocation('discard'),
'player_display_cards' => $this->cards->getCardsInLocation('player_display'),
'tiles' => $this->cards->getCardsInLocation('locslot'),
'tokens' => array_merge(
array_values($this->tokens->getTokensInLocation('box')),
array_values($this->tokens->getTokensInLocation('offtable')),
array_values($this->tokens->getTokensInLocation('agentarea_%')),
array_values($this->tokens->getTokensInLocation('cubes_%')), array_values($this->tokens->getTokensInLocation('discs_%')), array_values($this->tokens->getTokensInLocation('locslot_%')),
array_values($this->tokens->getTokensInLocation('penalty_%')),
array_values($this->tokens->getTokensInLocation('vp_%'))
)
);
}
function getPlayerCaseCards($player_id)
{
return $this->cards->getPlayerHand(self::getPlayerBefore($player_id));
}
function getStartPlayerId($minigame)
{
$start_player_no = $this->getStartPlayerNo($minigame);
$sql = "SELECT player_id FROM player WHERE player_no = $start_player_no";
return self::getUniqueValueFromDB($sql);
}
function getStartPlayerNo($minigame)
{
return (($minigame - 1) % self::getPlayersNumber()) + 1;
}
function notifyAnimate()
{
self::notifyAllPlayers("animate", "", array());
}
function notifyNewScores()
{
$scores = self::getCollectionFromDb("SELECT player_id, player_score FROM player", true);
self::notifyAllPlayers("newScores", "", array("scores" => $scores));
}
function replenishEvidenceDisplay()
{
if ($this->cards->countCardInLocation('evidence_display')
== $this->constants['EVIDENCE_DISPLAY_SIZE']) {
return;
}
if ($this->cards->countCardInLocation('deck') == 0
&& $this->cards->countCardInLocation('discard') > 0) {
$this->cards->moveAllCardsInLocation('discard', 'deck');
$this->cards->shuffle('deck');
}
if ($this->cards->countCardInLocation('deck') > 0) {
$newCard = $this->cards->pickCardForLocation("deck", "evidence_display");
self::notifyAllPlayers(
'evidenceReplenished', '',
array(
'card_id' => $newCard['id'],
'card_type' => $newCard['type_arg'],
'discard_is_empty' => $this->cards->countCardInLocation('discard') == 0,
));
} else {
self::notifyAllPlayers(
'evidenceExhausted',
clienttranslate('The evidence deck and discard pile are empty.'),
array());
}
}
protected function setCounter(&$array, $key, $value) {
$array[$key] = array('counter_name' => $key, 'counter_value' => $value);
}
function placeInvestigator($location_id)
{
self::checkAction("placeInvestigator");
$player_id = self::getActivePlayerId();
$player = self::loadPlayersBasicInfos()[$player_id];
$color = $this->constants['HEX2COLORNAME'][$player['player_color']];
$agent_area = "agentarea_{$location_id}";
$pis_in_supply = $this->tokens->countTokensInLocation("pi_supply_{$player_id}");
if ($pis_in_supply == 0) {
throw new BgaUserException(self::_("You have no investigators left."));
}
if (count($this->tokens->getTokensOfTypeInLocation("pi_{$color}_%", $agent_area))) {
throw new BgaUserException(self::_("You already have an investigator at this location."));
}
self::incStat(1, "investigators_used_" . self::getGameStateValue('minigame'), $player_id);
$number_of_loc_neighbors = count($this->locations[$location_id]['neighbors']);
$total_pis_used = $this->constants["PIS_PER_PLAYER"] - $pis_in_supply;
$current_val = self::getStat('avg_investigator_neighborhood', $player_id);
self::setStat(
($current_val * $total_pis_used + $number_of_loc_neighbors) / ($total_pis_used + 1),
'avg_investigator_neighborhood',
$player_id
);
$_temp = $this->tokens->pickTokensForLocation(1, "pi_supply_{$player_id}", $agent_area);
$pi_token = array_shift($_temp);
$counters = array();
$this->setCounter(
$counters, "remaining_investigators_$player_id",
$this->tokens->countTokensInLocation("pi_supply_$player_id"));
self::DbQuery("UPDATE player SET player_score_aux = player_score_aux - 100 WHERE player_id = $player_id");
$solution = $this->getPlayerCaseSolution($player_id);
$locslots = $this->locations[$location_id]['slots'];
$slot_ids = array_pluck($locslots, 'id');
$exact_matches = 0;
$adjacent_matches = 0;
$new_tokens = array();
$locslots_copy = array_values($locslots);
shuffle($locslots_copy);
foreach ($locslots_copy as $slot) {
$slot_id = $slot['id'];
$locslot_location = "locslot_{$slot_id}";
if (count($this->tokens->getTokensOfTypeInLocation("cube_{$color}_%", $locslot_location))) {
$adjacent_matches++;
continue;
}
if (count($this->tokens->getTokensOfTypeInLocation("disc_{$color}_%", $locslot_location))) {
$exact_matches++;
continue;
}
$_tiles = $this->cards->getCardsInLocation('locslot', $slot_id); $tile = array_shift($_tiles);
$tile_mid = $tile['type_arg'];
$mtile = $this->tiles[$tile_mid];
$full_match = $mtile['name'] == $solution[$mtile['tiletype']];
if ($full_match) {
$exact_matches++;
$disc = $this->tokens->getTokenOnTop("discs_{$player_id}");
$this->tokens->moveToken($disc['key'], $agent_area);
$new_tokens[] = $disc;
continue;
}
$adjacent_tile_names = $this->getAdjacentTileNames($location_id, "tile_{$mtile['tiletype']}");
$adjacent_match = false;
foreach ($solution as $name) {
if (in_array($name, $adjacent_tile_names)) {
$adjacent_match = true;
break;
}
}
if ($adjacent_match) {
$adjacent_matches++;
$cube = $this->tokens->getTokenOnTop("cubes_{$player_id}");
if (!$cube) {
}
$this->tokens->moveToken($cube['key'], $agent_area);
$new_tokens[] = $cube;
}
}
if ($exact_matches + $adjacent_matches == 0) {
$notif_msg = clienttranslate('${player_name} sends an investigator to ${location_name}: no matches.');
} else {
$notif_msg = clienttranslate('${player_name} sends an investigator to ${location_name}. Matches: ${n_exact} exact, ${n_adj} adjacent.');
}
self::notifyAllPlayers(
'placeToken',
$notif_msg,
array(
'i18n' => array('location_name'),
'counters' => $counters,
'token' => $pi_token,
'target_id' => $agent_area,
'player_name' => $player['player_name'],
'location_name' => $this->locations[$location_id]['name'],
'n_exact' => $exact_matches,
'n_adj' => $adjacent_matches,
)
);
if (count($new_tokens)) {
shuffle($new_tokens);
self::notifyAllPlayers(
'placeTokens', '',
array(
'_comment' => 'The tiles were checked in a random order; tokens are shuffled! No guessing!',
'tokens' => $new_tokens,
'target_id' => $agent_area,
)
);
}
$this->notifyAnimate();
$this->gamestate->nextState('nextTurn');
}
function selectEvidence($card_id)
{
self::checkAction("selectEvidence");
$currentCard = $this->cards->getCard($card_id);
if ($currentCard['location'] != "evidence_display") {
throw new BgaUserException(self::_("Card is not on display. Press F5 in case of problems."));
}
$player_id = self::getActivePlayerId();
$player = self::loadPlayersBasicInfos()[$player_id];
$color = $this->constants['HEX2COLORNAME'][$player['player_color']];
$tile = $this->getCorrespondingTile($currentCard['id']);
$target_id = "locslot_{$tile['location_arg']}";
if (count($this->tokens->getTokensOfTypeInLocation("%_{$color}_%", $target_id))) {
throw new BgaUserException(self::_("You already have a cube or disc there."));
}
$card_name = $this->cardBasis[$currentCard['type_arg']]['name'];
$location_id = $this->getLocationIdOfTile($tile);
$agent_area = "agentarea_{$location_id}";
self::incStat(1, "cards_taken_" . self::getGameStateValue('minigame'), $player_id);
$hand_cards = $this->cards->getPlayerHand($player_id);
if (in_array($currentCard['type_arg'] + 36, array_pluck($hand_cards, 'type_arg'))) {
self::incStat(1, "neighbor_case_cards_taken", $player_id);
}
$solution = $this->getPlayerCaseSolution($player_id);
if (in_array($card_name, $solution)) {
$discs = $this->tokens->getTokensOfTypeInLocation("disc_{$color}_%", $agent_area);
if (count($discs)) {
$disc = array_shift($discs);
} else {
$disc = $this->tokens->getTokenOnTop("discs_{$player_id}");
}
$this->tokens->moveToken($disc['key'], $target_id);
self::notifyAllPlayers(
'placeToken', '',
array(
'token' => $disc,
'target_id' => $target_id,
));
$this->cards->insertCardOnExtremePosition($card_id, "discard", true);
self::notifyAllPlayers(
'evidenceCorrect',
clienttranslate('${player_name} found one aspect of their case: ${card_name}!'),
array(
'i18n' => array('card_name'),
'card_id' => $card_id,
'card_name' => $card_name,
'card_type' => $currentCard['type_arg'],
'player_id' => $player_id,
'player_name' => self::getActivePlayerName()
));
$this->gamestate->nextState('nextTurn');
return;
}
$adjacent_tile_names = $this->getAdjacentTileNamesFromCard($card_id);
$match_name = null;
foreach ($solution as $casetype => $name) {
if (in_array($name, $adjacent_tile_names)) {
$match_name = $name;
$match_casetype = $casetype;
break;
}
}
if ($match_name) {
$cubes = $this->tokens->getTokensOfTypeInLocation("cube_{$color}_%", $agent_area);
if (count($cubes)) {
$cube = array_shift($cubes);
} else {
$cube = $this->tokens->getTokenOnTop("cubes_{$player_id}");
}
if (!$cube) {
throw new BgaUserException(self::_("No more cubes in your supply!"));
}
$this->cards->insertCardOnExtremePosition($card_id, "discard", true);
$this->tokens->moveToken($cube['key'], $target_id);
self::notifyAllPlayers(
'placeToken', '',
array(
'token' => $cube,
'target_id' => $target_id,
)
);
self::notifyAllPlayers(
'evidenceClose',
clienttranslate('${player_name} found out that ${card_name} is adjacent to the actual ${casetype}.'),
array(
'i18n' => array(
'card_name',
'casetype',
),
'card_id' => $card_id,
'card_name' => $card_name,
'card_type' => $currentCard['type_arg'],
'casetype' => $this->constants['CASETYPES'][$match_casetype],
'player_id' => $player_id,
'player_name' => self::getActivePlayerName(),
)
);
} else {
self::notifyAllPlayers(
'evidenceWrong',
clienttranslate('${player_name} found out that ${card_name} is unrelated to their case.'),
array(
'i18n' => array('card_name'),
'card_id' => $card_id,
'card_name' => $card_name,
'card_type' => $currentCard['type_arg'],
'player_id' => $player_id,
'player_name' => self::getActivePlayerName(),
));
$this->cards->moveCard($card_id, "player_display", $player_id);
}
$this->gamestate->nextState('nextTurn');
}
function solveCase($tile_ids) {
self::checkAction("solveCase");
$minigame = self::getGameStateValue('minigame');
$player_id = self::getActivePlayerId();
$color = $this->constants['HEX2COLORNAME'][self::getCurrentPlayerColor()];
$card_mids = array_pluck($this->getPlayerCaseCards($player_id), 'type_arg');
$tiles = $this->cards->getCards($tile_ids);
$tile_mids = array_pluck($tiles, 'type_arg');
$proposed_solution = array(
$this->tiles[$tile_mids[0]]['tiletype'] => $this->tiles[$tile_mids[0]]['name'],
$this->tiles[$tile_mids[1]]['tiletype'] => $this->tiles[$tile_mids[1]]['name'],
$this->tiles[$tile_mids[2]]['tiletype'] => $this->tiles[$tile_mids[2]]['name']
);
$solution = $this->getPlayerCaseSolution($player_id);
$player_correct = $proposed_solution == $solution;
if ($player_correct) {
$points_winnable = self::getGameStateValue('points_winnable');
self::DbQuery("
UPDATE player
SET player_score = player_score + " . $points_winnable . ",
player_solved_in_round = " . self::getGameStateValue('minigame_round') . "
WHERE player_id = $player_id
");
$this->tokens->moveToken("vp_{$color}_" . ($minigame - 1), "vp_{$points_winnable}");
self::setStat($points_winnable, "vp_{$minigame}", $player_id);
$cubes_on_board = $this->constants['CUBES_PER_PLAYER'] - $this->tokens->countTokensInLocation("cubes_{$player_id}");
$solved_mg_so_far = self::getStat("solved_minigames", $player_id);
$current_val = self::getStat('avg_cubes_to_solve', $player_id);
self::setStat(
($current_val * $solved_mg_so_far + $cubes_on_board) / ($solved_mg_so_far + 1),
'avg_cubes_to_solve',
$player_id
);
$discs_on_board = $this->constants['DISCS_PER_PLAYER'] - $this->tokens->countTokensInLocation("discs_{$player_id}");
$solved_mg_so_far = self::getStat("solved_minigames", $player_id);
$current_val = self::getStat('avg_discs_to_solve', $player_id);
self::setStat(
($current_val * $solved_mg_so_far + $discs_on_board) / ($solved_mg_so_far + 1),
'avg_discs_to_solve',
$player_id
);
self::incStat(1, "solved_minigames", $player_id);
$this->tokens->moveTokens(
array_pluck($this->tokens->getTokensOfTypeInLocation("cube_{$color}_%"), 'key'),
"cubes_{$player_id}");
$locslot_ids = array_pluck($tiles, 'location_arg');
foreach ($locslot_ids as $i => $locslot_id) {
$this->tokens->moveToken("disc_{$color}_{$i}", "locslot_{$locslot_id}");
}
$used_investigators = $this->tokens->getTokensOfTypeInLocation("pi_{$color}_%", 'agentarea_%');
if ($used_investigators) {
$this->tokens->moveTokens(array_pluck($used_investigators, 'key'), 'box');
}
self::notifyAllPlayers(
'placeTokens', '',
array('tokens' => array_values(array_merge(
$this->tokens->getTokensOfTypeInLocation("disc_{$color}_%"),
$this->tokens->getTokensOfTypeInLocation("vp_{$color}_%"),
$this->tokens->getTokensOfTypeInLocation("cube_{$color}_%"),
$this->tokens->getTokensOfTypeInLocation("pi_{$color}_%")
)))
);
$this->notifyAnimate();
self::notifyAllPlayers(
'playerSolved',
clienttranslate('${player_name} solved their case: ${suspect}, ${location}, ${crime}.'),
array(
'i18n' => array('crime', 'location', 'suspect'),
'player_id' => $player_id,
'player_name' => self::getActivePlayerName(),
'crime' => $solution['crime'],
'location' => $solution['location'],
'suspect' => $solution['suspect'],
)
);
} else {
self::DbQuery("
UPDATE player
SET player_score = player_score - 2,
player_score_aux = player_score_aux - 2,
player_penalty = player_penalty - 2
WHERE player_id = $player_id
");
self::incStat(-2, "penalty_" . self::getGameStateValue('minigame'), $player_id);
$total_pen = self::getUniqueValueFromDB("SELECT player_penalty FROM player WHERE player_id = $player_id");
$this->tokens->moveToken("penalty_{$color}", "penalty_" . min(abs($total_pen), 10));
self::notifyAllPlayers(
'placeToken', '',
array('token' => $this->tokens->getTokenInfo("penalty_{$color}"))
);
self::notifyAllPlayers(
'playerFailed',
clienttranslate('${player_name} tried to solve (${suspect}, ${location}, ${crime}), without success.'),
array(
'i18n' => array('crime', 'location', 'suspect'),
'crime' => $proposed_solution['crime'],
'location' => $proposed_solution['location'],
'suspect' => $proposed_solution['suspect'],
'player_name' => self::getActivePlayerName()
)
);
}
$this->notifyNewScores();
$this->gamestate->nextState('nextTurn');
}
function gotoNextMinigameOrEndGame()
{
$in_last_minigame = self::getGameStateValue('minigame') == $this->constants['MINIGAMES'];
return $this->gamestate->nextState($in_last_minigame ? 'endGame' : 'nextMinigame');
}
function argPlayerTurn()
{
$args = array(
'remainingInvestigators' => $this->tokens->countTokensInLocation("pi_supply_" . self::getActivePlayerId())
);
return $args;
}
function argStartMinigame()
{
$args = array('_private' => array());
$args = array_merge(
$args,
$this->getPublicGameInfos()
);
$players = self::loadPlayersBasicInfos();
foreach($players as $player_id => $player) {
$args['_private'][$player_id] = $this->getPrivateGameInfos($player_id);
}
return $args;
}
function st_setupMinigame()
{
self::incGameStateValue('minigame', 1);
$minigame = self::getGameStateValue('minigame');
self::setGameStateValue('minigame_round', 1);
self::setStat(1, "rounds_{$minigame}");
self::setGameStateValue('points_winnable', 7);
self::DbQuery("UPDATE `player` SET `player_solved_in_round` = NULL");
$this->cards->moveAllCardsInLocation(null, "offtable");
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('evidence'), 'id'), 'deck');
$this->cards->shuffle('deck');
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('crime'), 'id'), 'crime_deck');
$this->cards->shuffle('crime_deck');
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('location'), 'id'), 'location_deck');
$this->cards->shuffle('location_deck');
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('suspect'), 'id'), 'suspect_deck');
$this->cards->shuffle('suspect_deck');
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('tile_crime'), 'id'), 'cri_tile_d');
$this->cards->moveCards(array_pluck($this->cards->getCardsOfType('tile_suspect'), 'id'), 'sus_tile_d');
$this->cards->shuffle('cri_tile_d');
$this->cards->shuffle('sus_tile_d');
foreach($this->locations as $loc_id => $loc) {
$this->cards->pickCardForLocation('cri_tile_d', 'locslot', $loc['slots']['crime']['id']);
$this->cards->pickCardForLocation('sus_tile_d', 'locslot', $loc['slots']['suspect']['id']);
$temp = $this->cards->getCardsOfType('tile_location', 28 + $loc_id); $location_tile = array_shift($temp);
$this->cards->moveCard($location_tile['id'], 'locslot', $loc['slots']['location']['id']);
}
$this->cards->pickCardsForLocation($this->constants['EVIDENCE_DISPLAY_SIZE'], 'deck', 'evidence_display');
$used_investigators = $this->tokens->getTokensOfTypeInLocation('pi_%', 'agentarea_%');
if ($used_investigators) {
$this->tokens->moveTokens(array_pluck($used_investigators, 'key'), 'box');
}
$players = self::loadPlayersBasicInfos();
foreach($players as $player_id => $player) {
$this->cards->pickCard('crime_deck', $player_id);
$this->cards->pickCard('location_deck', $player_id);
$this->cards->pickCard('suspect_deck', $player_id);
$color = $this->constants['HEX2COLORNAME'][$player['player_color']];
$this->tokens->moveTokens(
array_pluck($this->tokens->getTokensOfTypeInLocation("cube_{$color}_%"), 'key'),
"cubes_{$player_id}");
$this->tokens->moveTokens(
array_pluck($this->tokens->getTokensOfTypeInLocation("disc_{$color}_%"), 'key'),
"discs_{$player_id}");
}
self::notifyAllPlayers("cleanBoard", "", array(
'tokens' => array_values(array_merge(
$this->tokens->getTokensInLocation('box'),
$this->tokens->getTokensInLocation('offtable'),
$this->tokens->getTokensInLocation('cubes_%'),
$this->tokens->getTokensInLocation('discs_%')
))
));
$this->notifyAnimate();
$this->gamestate->nextState(); }
function st_startMinigame()
{
$minigame = self::getGameStateValue('minigame');
self::notifyAllPlayers(
"message",
array(
1 => clienttranslate('The first of three mini-games starts.'),
2 => clienttranslate('The second mini-game starts.'),
3 => clienttranslate('The third and final mini-game starts.'),
)[$minigame],
array()
);
$next_player_no = $this->getStartPlayerNo($minigame);
$next_player_id = self::getUniqueValueFromDB("SELECT player_id FROM player WHERE player_no = $next_player_no");
$this->gamestate->changeActivePlayer($next_player_id);
self::incStat(1, "turns_number");
self::incStat(1, "turns_number", $next_player_id);
$this->gamestate->nextState(); }
function st_gameTurn()
{
$active_player_id = self::getActivePlayerId(); $minigame = self::getGameStateValue('minigame');
$unsolved_player_ids = self::getObjectListFromDB(
"SELECT player_id FROM player WHERE player_solved_in_round IS NULL", true);
if (count($unsolved_player_ids) == 0) {
self::notifyAllPlayers(
'minigameEnds',
clienttranslate('All players solved their cases! This ends the mini-game.'),
array()
);
return $this->gotoNextMinigameOrEndGame();
}
$round_over = false;
$start_player_id = $this->getStartPlayerId($minigame);
$next_player_id = $active_player_id;
do {
$next_player_id = self::getPlayerAfter($next_player_id);
if ($round_over || $next_player_id == $start_player_id) $round_over = true;
} while (!in_array($next_player_id, $unsolved_player_ids));
if ($round_over) {
if (count($unsolved_player_ids) == 1) {
$unsolved_player_id = array_shift($unsolved_player_ids);
$unsolved_player = self::loadPlayersBasicInfos()[$unsolved_player_id];
$unsolved_color = $this->constants['HEX2COLORNAME'][$unsolved_player['player_color']];
$token_key = "vp_{$unsolved_color}_" . ($minigame - 1);
$token_target = "vp_0";
$this->tokens->moveToken($token_key, $token_target);
self::notifyAllPlayers(
'placeToken',
'',
array('token' => array("key" => $token_key, "location" => $token_target))
);
$solution = $this->getPlayerCaseSolution($unsolved_player_id);
self::notifyAllPlayers(
'message',
clienttranslate('The round is over and only 1 player did not solve yet (${player_name}: ${suspect}, ${location}, ${crime}).'),
array(
'i18n' => array('crime', 'location', 'suspect'),
'player_name' => $unsolved_player['player_name'],
'crime' => $solution['crime'],
'location' => $solution['location'],
'suspect' => $solution['suspect'],
)
);
self::notifyAllPlayers(
'minigameEnds',
clienttranslate('This ends the mini-game.'),
array()
);
return $this->gotoNextMinigameOrEndGame();
}
$round = self::getGameStateValue('minigame_round');
$sql = "SELECT COUNT(player_id) FROM player WHERE player_solved_in_round = $round";
if (self::getUniqueValueFromDB($sql)) {
self::setGameStateValue(
'points_winnable',
max(0, self::getGameStateValue('points_winnable') - 2));
}
self::incGameStateValue('minigame_round', 1);
self::incStat(1, "rounds_{$minigame}");
}
$this->replenishEvidenceDisplay();
$this->gamestate->changeActivePlayer($next_player_id);
self::giveExtraTime($next_player_id);
self::incStat(1, "turns_number");
self::incStat(1, "turns_number", $next_player_id);
$this->gamestate->nextState('nextPlayer'); }
function zombieTurn($state, $active_player)
{
$statename = $state['name'];
$player_id = $active_player;
if ($statename === "playerTurn") {
self::DbQuery("
UPDATE player
SET player_solved_in_round = " . self::getGameStateValue('minigame_round') . "
WHERE player_id = $player_id
");
self::notifyAllPlayers(
'playerSolved',
'',
array(
'player_id' => $player_id,
)
);
$this->gamestate->nextState('nextTurn');
return;
}
throw new feException("Zombie mode not supported at this game state: " . $statename);
}
function upgradeTableDb( $from_version )
{
}
}