HTIL7GWVKJ6WRRRQEEFGMQL6GWTFW2TP7FI34DLXI4DK4QS4HXSAC
J6J74CCDLZIY2SBU6O7DCX7QUO7LVNBFOZJ7F3VW4OPRPHKQ62OAC
OPI6W7BO4V3R5WGEVN4USOHVPUV5PVWGKYBMCRU4VQOI7F7RCF5QC
MYLF7NEGFT6YYO6OEK22FLAS6Y2V3YGU4CQIMBE6APOLGLTOFNRQC
NRK37A4NNGLF53STW5RJDHUEP6OKKJORJZL3HYVOKD7OW4QBO5XAC
OLS2OW4YBKIEBURLW2LZDHHFGBGILOXQHHZBW7ED37MKISEGJKOAC
JPHC6PBQJCGZ3B4LKX7C3XZOYP6UTHWV5PU5EXZ27TTXYZY3AG4AC
ZXBDOUETFSXTFY6J5LIMXBCNMDW6NTUTZ7HZZ6ND7BNUKISR74FAC
EZYBNJFUNOS3LIU6ILXN7MFKLVW4SDXW6K5T4YFKFZ2R4UWSFUXAC
V5OW3Q4IOFG5YIRAWE7J3HDTEBOX2EW7AHVHKFH2XG7I2XXH6SMAC
ISWDRBPZCEGJSXNXHML7D7XDW4N3Z7MXOF4OS7UHC6U2GVXFHYZQC
border-width:1px !important;
box-shadow: 0 0 4px 6px rgb(255, 255, 255) !important;
border:solid 1px red; /* because it's an instant action */
box-shadow: 0 0 2px 2px rgb(255, 255, 255) !important;
border:solid 2px red; /* because it's an instant action */
border-width:2px !important;
.locslot .cube20,
.locslot .cube25 {
z-index:100;
position: absolute;
}
.locslot .locslot_cubes {
position: absolute;
bottom:-4px;
left:-4px;
height:50px;
outline:solid 1px red;
}
.locslot .locslot_discs {
position: absolute;
bottom:-4px;
right:-4px;
height:50px;
outline:solid 1px green;
}
.locslot .cube20:nth-of-type(1) { bottom:0; z-index:200; }
.locslot .cube20:nth-of-type(2) { bottom:0; left:17px; z-index:200; }
.locslot .cube20:nth-of-type(3) { bottom:13px; z-index:201; }
.locslot .cube20:nth-of-type(4) { bottom:13px; left:17px; z-index:201; }
.locslot .cube20:nth-of-type(5) { bottom:26px; z-index:203; }
.cube20 {
height: 20px;
width: 20px;
background-image: url("img/common/cubes20_hc.png");
}
.cube20_blue { background-position: -20px 0px }
.cube20_red { background-position: -40px 0px }
.cube20_yellow { background-position: -60px 0px }
.cube20_green { background-position: -80px 0px }
.cube20_white { background-position: -100px 0px }
.cube20_black { background-position: -120px 0px }
.cube20_brown { background-position: -140px 0px }
.cube20_purple { background-position: -160px 0px }
.cube20_pink { background-position: -180px 0px }
.cube20_orange { background-position: -200px 0px }
.disc30 {
height: 30px;
width: 30px;
background-image: url("img/common/discs30_hc.png");
}
.disc30_blue { background-position: 0px 0px }
.disc30_green { background-position: -30px 0px }
.disc30_beige { background-position: -60px 0px }
.disc30_orange { background-position: -90px 0px }
.disc30_white { background-position: -120px 0px }
.disc30_black { background-position: -150px 0px }
.disc30_red { background-position: -180px 0px }
.disc30_yellow { background-position: -210px 0px }
.disc30_gray { background-position: -240px 0px }
.disc30_purple { background-position: -270px 0px }
* For the given location_id, return all adjacent tiles' names (includes
* all case aspects).
*/
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');
}
/**
$neighbor_mids = $this->locations[$location_mid]['neighbors'];
$adjacent_slot_ids = array_pluck(
array_flatten(array_pluck(
array_filter_by_keys($this->locations, $neighbor_mids),
'slots')),
'id');
$sql = "SELECT card_type_arg FROM `card`
WHERE card_type = '{$tile['type']}'
AND card_location_arg IN (" . implode(',', $adjacent_slot_ids) . ")";
$tile_mids = self::getObjectListFromDB($sql, true);
return array_pluck(array_filter_by_keys($this->tiles, $tile_mids), 'name');
return $this->getAdjacentTileNames($location_mid, $tile['type']);
$locslots = $this->locations[$location_id]['slots'];
$slot_ids = array_pluck($locslots, 'id');
$target = "agentarea_{$location_id}";
// IMPORTANT! Even if we match, we must not notify/place right away, as
// we must not give away which of the aspects the new token(s) refer
// to. So we collect the new tokens first. Then shuffle this list and
// notify/place all at the same time. Also we go through the slots in a
// random order, so we don't acidentally pick up tokens (they are
// id'ed!) in a revealing order.
$new_tokens = array();
$locslots_copy = array_values($locslots);
shuffle($locslots_copy);
foreach ($locslots_copy as $slot) {
$slot_id = $slot['id'];
// TODO: Place investigator token here.
// TODO: get the 3 tiles at this location and do a check for each one of them
// First, check if there's a disc/cube of player color on it
// already. If so, we have as much information for this aspect as
// we can get: continue.
if (count($this->tokens->getTokensOfTypeInLocation("cube_{$color}_%", $target)) ||
count($this->tokens->getTokensOfTypeInLocation("disc_{$color}_%", $target))) {
continue;
}
// Check for a match with the solution.
$_tiles = $this->cards->getCardsInLocation('locslot', $slot_id); // only 1, but php...
$tile = array_shift($_tiles);
$tile_mid = $tile['type_arg'];
$mtile = $this->tiles[$tile_mid]; // material tile
// Full match: put disc into the agent area
$full_match = $mtile['name'] == $solution[$mtile['tiletype']];
if ($full_match) {
$disc = $this->tokens->getTokenOnTop("discs_{$player_id}");
self::dump('disc', $disc);
$this->tokens->moveToken($disc['key'], $target);
$new_tokens[] = $disc;
// Done with this location slot
continue;
}
// Not a full match; check adjacent locations now.
$adjacent_tile_names = $this->getAdjacentTileNames($location_id);
$close_match = false;
foreach ($solution as $name) {
if (in_array($name, $adjacent_tile_names)) {
$close_match = true;
break;
}
}
// Close match: put cube on tile.
if ($close_match) {
$cube = $this->tokens->getTokenOnTop("cubes_{$player_id}");
if (!$cube) {
// TODO: warning/error?? Player run out of cubes.
}
$this->tokens->moveToken($cube['key'], $target);
$new_tokens[] = $cube;
}
}
// TODO: in case of 3 full matches, we could place the tokens on the
// tiles directly. More of a convenience feature, however.
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' => $target,
));
} else {
// TODO: notify that the investigator found no new clues...
}
}
},
/**
* Place token on the table.
*/
placeToken: function (token, target_id) {
var key = token.key;
var keyparts = key.split('_');
var ttype = keyparts[0]; // cube, disc
var color = keyparts[1];
var html;
if (ttype == 'cube') {
var html = '<div id="' + key + '" class="cube20 cube20_' + color + '"></div>';
} else {
var html = '<div id="' + key + '" class="disc30 disc30_' + color + '"></div>';
}
if (!target_id) {
target_id = token.location;
// No target id given. Then derive it from the type by some easy rules.
if (token.location.startsWith('locslot')) {
target_id += '_' + ttype + 's'; // "locslot_xxx_(cube|disc)s"
}