Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc19d6be53 | ||
|
|
c54023083a | ||
|
|
af6861350d | ||
|
|
53da1e48f8 | ||
|
|
019c3110f9 | ||
|
|
17a4a01f6d |
@@ -143,6 +143,9 @@ deploy: ## Pubblica l'ultimo commit: test, immagine costruita e provata qui, spe
|
|||||||
scp -q $(DEPLOY_TMP)/docker-compose.homeserver.yml $(DEPLOY_HOST):$(DEPLOY_DIR)/docker-compose.yml
|
scp -q $(DEPLOY_TMP)/docker-compose.homeserver.yml $(DEPLOY_HOST):$(DEPLOY_DIR)/docker-compose.yml
|
||||||
docker save $(IMAGE):latest | gzip | ssh $(DEPLOY_HOST) 'gunzip | docker load -q && cd $(DEPLOY_DIR) && docker compose up -d && docker image prune -f >/dev/null'
|
docker save $(IMAGE):latest | gzip | ssh $(DEPLOY_HOST) 'gunzip | docker load -q && cd $(DEPLOY_DIR) && docker compose up -d && docker image prune -f >/dev/null'
|
||||||
rm -rf $(DEPLOY_TMP)
|
rm -rf $(DEPLOY_TMP)
|
||||||
|
@# Sul server il prune toglie l'immagine vecchia; qui restano solo latest e quella appena pubblicata.
|
||||||
|
@docker image ls $(IMAGE) --format '{{.Tag}}' | grep -v -x -e latest -e $$(git rev-parse --short HEAD) | \
|
||||||
|
xargs -r -I{} docker image rm -f $(IMAGE):{} >/dev/null
|
||||||
@echo "→ $(DEPLOY_URL) ($$(git log -1 --format='%h %s'))"
|
@echo "→ $(DEPLOY_URL) ($$(git log -1 --format='%h %s'))"
|
||||||
|
|
||||||
deploy-logs: ## Log del container sul server di deploy
|
deploy-logs: ## Log del container sul server di deploy
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"image": "arcu.png",
|
||||||
|
"height": 120,
|
||||||
|
"frames": {
|
||||||
|
"stand": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 42,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.9,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"blink": {
|
||||||
|
"x": 44,
|
||||||
|
"y": 0,
|
||||||
|
"w": 42,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.3,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"talk": {
|
||||||
|
"x": 88,
|
||||||
|
"y": 0,
|
||||||
|
"w": 42,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.9,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"explain": {
|
||||||
|
"x": 132,
|
||||||
|
"y": 0,
|
||||||
|
"w": 66,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 24.3,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"laugh": {
|
||||||
|
"x": 200,
|
||||||
|
"y": 0,
|
||||||
|
"w": 46,
|
||||||
|
"h": 121,
|
||||||
|
"ax": 17.8,
|
||||||
|
"ay": 121
|
||||||
|
},
|
||||||
|
"point": {
|
||||||
|
"x": 248,
|
||||||
|
"y": 0,
|
||||||
|
"w": 65,
|
||||||
|
"h": 121,
|
||||||
|
"ax": 24.9,
|
||||||
|
"ay": 121
|
||||||
|
},
|
||||||
|
"shrug": {
|
||||||
|
"x": 315,
|
||||||
|
"y": 0,
|
||||||
|
"w": 78,
|
||||||
|
"h": 121,
|
||||||
|
"ax": 36.9,
|
||||||
|
"ay": 121
|
||||||
|
},
|
||||||
|
"arms_crossed": {
|
||||||
|
"x": 395,
|
||||||
|
"y": 0,
|
||||||
|
"w": 43,
|
||||||
|
"h": 121,
|
||||||
|
"ax": 19.4,
|
||||||
|
"ay": 121
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"image": "barondeduke.png",
|
||||||
|
"height": 120,
|
||||||
|
"frames": {
|
||||||
|
"stand": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 47,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 21.7,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"blink": {
|
||||||
|
"x": 49,
|
||||||
|
"y": 0,
|
||||||
|
"w": 47,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 21.5,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"talk": {
|
||||||
|
"x": 98,
|
||||||
|
"y": 0,
|
||||||
|
"w": 48,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 21.8,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"explain": {
|
||||||
|
"x": 148,
|
||||||
|
"y": 0,
|
||||||
|
"w": 61,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 23.9,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"laugh": {
|
||||||
|
"x": 211,
|
||||||
|
"y": 0,
|
||||||
|
"w": 53,
|
||||||
|
"h": 117,
|
||||||
|
"ax": 26.6,
|
||||||
|
"ay": 117
|
||||||
|
},
|
||||||
|
"cards": {
|
||||||
|
"x": 266,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 118,
|
||||||
|
"ax": 24.1,
|
||||||
|
"ay": 118
|
||||||
|
},
|
||||||
|
"shrug": {
|
||||||
|
"x": 324,
|
||||||
|
"y": 0,
|
||||||
|
"w": 73,
|
||||||
|
"h": 117,
|
||||||
|
"ax": 35.7,
|
||||||
|
"ay": 117
|
||||||
|
},
|
||||||
|
"facepalm": {
|
||||||
|
"x": 399,
|
||||||
|
"y": 0,
|
||||||
|
"w": 53,
|
||||||
|
"h": 117,
|
||||||
|
"ax": 27.3,
|
||||||
|
"ay": 117
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"image": "tozzino.png",
|
||||||
|
"height": 120,
|
||||||
|
"frames": {
|
||||||
|
"stand": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 40,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.6,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"blink": {
|
||||||
|
"x": 42,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.5,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"talk": {
|
||||||
|
"x": 85,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.6,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"explain": {
|
||||||
|
"x": 128,
|
||||||
|
"y": 0,
|
||||||
|
"w": 65,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 24.4,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"laugh": {
|
||||||
|
"x": 195,
|
||||||
|
"y": 0,
|
||||||
|
"w": 40,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 20.1,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"x": 237,
|
||||||
|
"y": 0,
|
||||||
|
"w": 51,
|
||||||
|
"h": 119,
|
||||||
|
"ax": 25.5,
|
||||||
|
"ay": 119
|
||||||
|
},
|
||||||
|
"look_far": {
|
||||||
|
"x": 290,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 120,
|
||||||
|
"ax": 27.9,
|
||||||
|
"ay": 120
|
||||||
|
},
|
||||||
|
"bounce": {
|
||||||
|
"x": 348,
|
||||||
|
"y": 0,
|
||||||
|
"w": 67,
|
||||||
|
"h": 117,
|
||||||
|
"ax": 21.1,
|
||||||
|
"ay": 117
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -623,6 +623,38 @@
|
|||||||
"file": "andrea-e47b3cebb5.mp3",
|
"file": "andrea-e47b3cebb5.mp3",
|
||||||
"sec": 4.87
|
"sec": 4.87
|
||||||
},
|
},
|
||||||
|
"andrea|Il BaronDeDuke, col cappello da cowboy. Ha l'aria di uno che aspetta qualcuno, e che spera non lo veda nessuno.": {
|
||||||
|
"file": "andrea-0bd1b0f32e.mp3",
|
||||||
|
"sec": 8.7
|
||||||
|
},
|
||||||
|
"andrea|Barone! Che ci fai qui?": {
|
||||||
|
"file": "andrea-83abae1419.mp3",
|
||||||
|
"sec": 2.91
|
||||||
|
},
|
||||||
|
"barondeduke|Aspetto uno che mi deve riportare le mutande.": {
|
||||||
|
"file": "barondeduke-3ea60b7083.mp3",
|
||||||
|
"sec": 4.12
|
||||||
|
},
|
||||||
|
"andrea|Le... mutande?": {
|
||||||
|
"file": "andrea-f83ef038f8.mp3",
|
||||||
|
"sec": 3.94
|
||||||
|
},
|
||||||
|
"barondeduke|Le ho perse a poker, ieri sera.": {
|
||||||
|
"file": "barondeduke-5542a06624.mp3",
|
||||||
|
"sec": 1.65
|
||||||
|
},
|
||||||
|
"barondeduke|Avevo due donne e due jack. Entra la terza donna, e penso: «Male male si divide».": {
|
||||||
|
"file": "barondeduke-d4ff967179.mp3",
|
||||||
|
"sec": 7.64
|
||||||
|
},
|
||||||
|
"andrea|E invece?": {
|
||||||
|
"file": "andrea-435306c242.mp3",
|
||||||
|
"sec": 0.67
|
||||||
|
},
|
||||||
|
"barondeduke|L'altro aveva colore.": {
|
||||||
|
"file": "barondeduke-238f525535.mp3",
|
||||||
|
"sec": 1.65
|
||||||
|
},
|
||||||
"andrea|La tribuna bianca e rossa. La domenica qui c'è più tifo che al Palio. Quasi.": {
|
"andrea|La tribuna bianca e rossa. La domenica qui c'è più tifo che al Palio. Quasi.": {
|
||||||
"file": "andrea-a50071085e.mp3",
|
"file": "andrea-a50071085e.mp3",
|
||||||
"sec": 6.84
|
"sec": 6.84
|
||||||
@@ -767,6 +799,18 @@
|
|||||||
"file": "andrea-bb70dea2a6.mp3",
|
"file": "andrea-bb70dea2a6.mp3",
|
||||||
"sec": 2.15
|
"sec": 2.15
|
||||||
},
|
},
|
||||||
|
"andrea|L'Arcu, il mio amico di sempre. Davanti al circolino, come se ci fosse nato.": {
|
||||||
|
"file": "andrea-8ddab2d024.mp3",
|
||||||
|
"sec": 5.79
|
||||||
|
},
|
||||||
|
"andrea|Arcu! Ma che è sempre là, Borgo?": {
|
||||||
|
"file": "andrea-aaeb40d873.mp3",
|
||||||
|
"sec": 3.42
|
||||||
|
},
|
||||||
|
"arcu|E chi lo sposta???": {
|
||||||
|
"file": "arcu-64735a5925.mp3",
|
||||||
|
"sec": 0.97
|
||||||
|
},
|
||||||
"andrea|Il ponte vecchio, coi suoi due archi. Chissà quante piene ha visto passare: io al primo temporale cerco riparo.": {
|
"andrea|Il ponte vecchio, coi suoi due archi. Chissà quante piene ha visto passare: io al primo temporale cerco riparo.": {
|
||||||
"file": "andrea-01ec1d03ec.mp3",
|
"file": "andrea-01ec1d03ec.mp3",
|
||||||
"sec": 7.67
|
"sec": 7.67
|
||||||
@@ -935,6 +979,30 @@
|
|||||||
"file": "andrea-f5d55187e8.mp3",
|
"file": "andrea-f5d55187e8.mp3",
|
||||||
"sec": 5.11
|
"sec": 5.11
|
||||||
},
|
},
|
||||||
|
"andrea|Il Tozzino, vestito di bianco da capo a piedi, con la racchetta. Guarda l'orologio ogni dieci secondi.": {
|
||||||
|
"file": "andrea-3c32888a15.mp3",
|
||||||
|
"sec": 8.1
|
||||||
|
},
|
||||||
|
"andrea|Tozzino! Che fai lì davanti al cancello?": {
|
||||||
|
"file": "andrea-1fe1ee6236.mp3",
|
||||||
|
"sec": 3.1
|
||||||
|
},
|
||||||
|
"tozzino|Aspetto Federer. Oggi si fa una partita.": {
|
||||||
|
"file": "tozzino-c3898e9bdb.mp3",
|
||||||
|
"sec": 3.23
|
||||||
|
},
|
||||||
|
"andrea|Federer? Roger Federer?": {
|
||||||
|
"file": "andrea-70f150bdec.mp3",
|
||||||
|
"sec": 2.67
|
||||||
|
},
|
||||||
|
"tozzino|E chi sennò? Ha chiamato ora: è imbottigliato in Bolognese.": {
|
||||||
|
"file": "tozzino-d51159c364.mp3",
|
||||||
|
"sec": 4.08
|
||||||
|
},
|
||||||
|
"tozzino|Se non arriva, la vinco a tavolino.": {
|
||||||
|
"file": "tozzino-c06cc01612.mp3",
|
||||||
|
"sec": 2.06
|
||||||
|
},
|
||||||
"andrea|La Carza. A maggio c'è poca acqua: d'estate si scendeva a fare le dighe coi sassi.": {
|
"andrea|La Carza. A maggio c'è poca acqua: d'estate si scendeva a fare le dighe coi sassi.": {
|
||||||
"file": "andrea-91be1de49b.mp3",
|
"file": "andrea-91be1de49b.mp3",
|
||||||
"sec": 6.99
|
"sec": 6.99
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "leggenda-del-regolo",
|
"name": "leggenda-del-regolo",
|
||||||
"version": "0.1.1",
|
"version": "0.1.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+6
-1
@@ -217,6 +217,7 @@ function filterMap() {
|
|||||||
$('map-none').hidden = shown > 0;
|
$('map-none').hidden = shown > 0;
|
||||||
$('map-none').textContent = `Nessun posto con «${query.trim()}»`;
|
$('map-none').textContent = `Nessun posto con «${query.trim()}»`;
|
||||||
drawRoads();
|
drawRoads();
|
||||||
|
$('map-scroll').scrollTo(0, 0); // i posti rimasti stanno in cima: niente mappa vuota scorsa in fondo
|
||||||
}
|
}
|
||||||
$('map-search').addEventListener('input', filterMap);
|
$('map-search').addEventListener('input', filterMap);
|
||||||
$('map-search').addEventListener('keydown', e => {
|
$('map-search').addEventListener('keydown', e => {
|
||||||
@@ -226,7 +227,11 @@ $('map-search').addEventListener('keydown', e => {
|
|||||||
});
|
});
|
||||||
// Le strade fra le schede, misurate dopo l'impaginazione (e di nuovo se la finestra cambia misura).
|
// Le strade fra le schede, misurate dopo l'impaginazione (e di nuovo se la finestra cambia misura).
|
||||||
function drawRoads() {
|
function drawRoads() {
|
||||||
const board = $('map-board'), svg = $('map-roads'), origin = board.getBoundingClientRect();
|
const board = $('map-board'), svg = $('map-roads');
|
||||||
|
// Prima si toglie la misura vecchia: il disegno delle strade, grande quanto la mappa intera, la terrebbe lunga
|
||||||
|
// anche dopo un filtro.
|
||||||
|
svg.style.width = svg.style.height = '0';
|
||||||
|
const origin = board.getBoundingClientRect();
|
||||||
const center = id => {
|
const center = id => {
|
||||||
const card = board.querySelector(`[data-scene="${id}"]`);
|
const card = board.querySelector(`[data-scene="${id}"]`);
|
||||||
const r = card && !card.hidden && !card.parentElement.hidden && card.querySelector('img').getBoundingClientRect();
|
const r = card && !card.hidden && !card.parentElement.hidden && card.querySelector('img').getBoundingClientRect();
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ export const story = {
|
|||||||
leone: { name: 'Il leone', color: '#f0a070' },
|
leone: { name: 'Il leone', color: '#f0a070' },
|
||||||
san_pietro: { name: 'San Pietro', color: '#e8e0c8' },
|
san_pietro: { name: 'San Pietro', color: '#e8e0c8' },
|
||||||
tosca: { name: 'La Tosca', color: '#ffb0d0' },
|
tosca: { name: 'La Tosca', color: '#ffb0d0' },
|
||||||
|
arcu: { name: 'L\'Arcu', color: '#7fb2ff', sprite: 'assets/characters/arcu.json' },
|
||||||
|
tozzino: { name: 'Il Tozzino', color: '#c8f5b0', sprite: 'assets/characters/tozzino.json' },
|
||||||
|
barondeduke: { name: 'Il BaronDeDuke', color: '#e0c090', sprite: 'assets/characters/barondeduke.json' },
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
squama: {
|
squama: {
|
||||||
@@ -974,6 +977,9 @@ export const story = {
|
|||||||
walk: [[0, 360], [0, 330], [300, 262], [430, 222], [600, 210], [640, 212], [640, 360]],
|
walk: [[0, 360], [0, 330], [300, 262], [430, 222], [600, 210], [640, 212], [640, 360]],
|
||||||
depth: [[210, 0.4], [360, 1.15]],
|
depth: [[210, 0.4], [360, 1.15]],
|
||||||
entry: [100, 340],
|
entry: [100, 340],
|
||||||
|
// Il BaronDeDuke nella discesa verso la polisportiva: aspetta chi gli riporta le mutande perse a poker.
|
||||||
|
speakers: { barondeduke: [230, 243] },
|
||||||
|
people: [{ who: 'barondeduke', at: [230, 300], facing: -1, idle: 'stand', talk: ['talk', 'explain'], blink: 'blink', every: 4.6 }],
|
||||||
hotspots: [
|
hotspots: [
|
||||||
{
|
{
|
||||||
id: 'campo', name: 'campo secondario', box: [140, 160, 240, 60], at: [330, 254], face: 'up',
|
id: 'campo', name: 'campo secondario', box: [140, 160, 240, 60], at: [330, 254], face: 'up',
|
||||||
@@ -1007,6 +1013,21 @@ export const story = {
|
|||||||
{
|
{
|
||||||
id: 'al_principale', name: 'indietro, verso il campo principale', box: [0, 330, 640, 30], at: [100, 354], exit: { scene: 'polisportiva_principale', at: [620, 330], face: 'left' },
|
id: 'al_principale', name: 'indietro, verso il campo principale', box: [0, 330, 640, 30], at: [100, 354], exit: { scene: 'polisportiva_principale', at: [620, 330], face: 'left' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'barondeduke', name: 'il BaronDeDuke', box: [212, 245, 36, 55], at: [190, 316], face: 'right', default: 'talk',
|
||||||
|
actions: {
|
||||||
|
look: [{ say: 'Il BaronDeDuke, col cappello da cowboy. Ha l\'aria di uno che aspetta qualcuno, e che spera non lo veda nessuno.' }],
|
||||||
|
talk: [
|
||||||
|
{ say: 'Barone! Che ci fai qui?' },
|
||||||
|
{ say: 'Aspetto uno che mi deve riportare le mutande.', who: 'barondeduke' },
|
||||||
|
{ say: 'Le... mutande?' },
|
||||||
|
{ say: 'Le ho perse a poker, ieri sera.', who: 'barondeduke', pose: 'facepalm' },
|
||||||
|
{ say: 'Avevo due donne e due jack. Entra la terza donna, e penso: «Male male si divide».', who: 'barondeduke', pose: 'cards' },
|
||||||
|
{ say: 'E invece?' },
|
||||||
|
{ say: 'L\'altro aveva colore.', who: 'barondeduke', pose: 'shrug' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
polisportiva_principale: {
|
polisportiva_principale: {
|
||||||
@@ -1248,6 +1269,9 @@ export const story = {
|
|||||||
walk: [[0, 360], [0, 335], [60, 300], [400, 280], [640, 250], [640, 360]],
|
walk: [[0, 360], [0, 335], [60, 300], [400, 280], [640, 250], [640, 360]],
|
||||||
depth: [[250, 1.0], [360, 1.5]],
|
depth: [[250, 1.0], [360, 1.5]],
|
||||||
entry: [320, 330],
|
entry: [320, 330],
|
||||||
|
// L'Arcu davanti al circolino, accanto alla fontanella (sprite di Codex, 25 ottobre).
|
||||||
|
speakers: { arcu: [418, 214] },
|
||||||
|
people: [{ who: 'arcu', at: [418, 292], facing: -1, idle: 'stand', talk: ['talk', 'explain'], blink: 'blink', every: 4.2 }],
|
||||||
hotspots: [
|
hotspots: [
|
||||||
{
|
{
|
||||||
id: 'circolo', name: 'il circolino', box: [100, 0, 380, 205], at: [300, 290], face: 'up',
|
id: 'circolo', name: 'il circolino', box: [100, 0, 380, 205], at: [300, 290], face: 'up',
|
||||||
@@ -1293,6 +1317,13 @@ export const story = {
|
|||||||
look: [{ say: 'La strada va avanti verso Tagliaferro.' }],
|
look: [{ say: 'La strada va avanti verso Tagliaferro.' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'arcu', name: 'l\'Arcu', box: [400, 216, 38, 76], at: [362, 300], face: 'right', default: 'talk',
|
||||||
|
actions: {
|
||||||
|
look: [{ say: 'L\'Arcu, il mio amico di sempre. Davanti al circolino, come se ci fosse nato.' }],
|
||||||
|
talk: [{ say: 'Arcu! Ma che è sempre là, Borgo?' }, { say: 'E chi lo sposta???', who: 'arcu', pose: 'shrug' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
campomigliaio_ponte: {
|
campomigliaio_ponte: {
|
||||||
@@ -1557,6 +1588,9 @@ export const story = {
|
|||||||
walk: [[60, 360], [250, 262], [300, 218], [350, 218], [420, 262], [600, 360]],
|
walk: [[60, 360], [250, 262], [300, 218], [350, 218], [420, 262], [600, 360]],
|
||||||
depth: [[218, 0.55], [360, 1.65]],
|
depth: [[218, 0.55], [360, 1.65]],
|
||||||
entry: [300, 350],
|
entry: [300, 350],
|
||||||
|
// Il Tozzino davanti al cancello del tennis, in tenuta, che aspetta Federer.
|
||||||
|
speakers: { tozzino: [440, 222] },
|
||||||
|
people: [{ who: 'tozzino', at: [440, 300], facing: -1, idle: 'stand', talk: ['talk', 'explain'], blink: 'blink', every: 3.9 }],
|
||||||
hotspots: [
|
hotspots: [
|
||||||
{
|
{
|
||||||
id: 'muro', name: 'casa dell\'ing. Tozzi', box: [0, 120, 200, 140], at: [190, 300], face: 'left',
|
id: 'muro', name: 'casa dell\'ing. Tozzi', box: [0, 120, 200, 140], at: [190, 300], face: 'left',
|
||||||
@@ -1576,6 +1610,19 @@ export const story = {
|
|||||||
{
|
{
|
||||||
id: 'alla_pieve', name: 'su verso la Pieve', box: [0, 330, 640, 30], at: [300, 354], exit: { scene: 'pieve', at: [95, 250], face: 'right' },
|
id: 'alla_pieve', name: 'su verso la Pieve', box: [0, 330, 640, 30], at: [300, 354], exit: { scene: 'pieve', at: [95, 250], face: 'right' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'tozzino', name: 'il Tozzino', box: [422, 224, 36, 76], at: [392, 312], face: 'right', default: 'talk',
|
||||||
|
actions: {
|
||||||
|
look: [{ say: 'Il Tozzino, vestito di bianco da capo a piedi, con la racchetta. Guarda l\'orologio ogni dieci secondi.' }],
|
||||||
|
talk: [
|
||||||
|
{ say: 'Tozzino! Che fai lì davanti al cancello?' },
|
||||||
|
{ say: 'Aspetto Federer. Oggi si fa una partita.', who: 'tozzino', pose: 'explain' },
|
||||||
|
{ say: 'Federer? Roger Federer?' },
|
||||||
|
{ say: 'E chi sennò? Ha chiamato ora: è imbottigliato in Bolognese.', who: 'tozzino', pose: 'watch' },
|
||||||
|
{ say: 'Se non arriva, la vinco a tavolino.', who: 'tozzino', pose: 'look_far' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
ponticino: {
|
ponticino: {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// La versione del gioco: la cambia scripts/bump.sh, insieme a package.json.
|
// La versione del gioco: la cambia scripts/bump.sh, insieme a package.json.
|
||||||
export const VERSION = '0.1.1';
|
export const VERSION = '0.1.3';
|
||||||
|
|
||||||
// Immagini, musiche e voci con ?v=<versione>: a ogni versione l'indirizzo cambia e il browser le riscarica,
|
// Immagini, musiche e voci con ?v=<versione>: a ogni versione l'indirizzo cambia e il browser le riscarica,
|
||||||
// mentre fra una versione e l'altra le tiene in cache a lungo (nginx.conf).
|
// mentre fra una versione e l'altra le tiene in cache a lungo (nginx.conf).
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
// ritagli dei nuovi effetti, pause degli animali e nessuna modifica alla partita.
|
// ritagli dei nuovi effetti, pause degli animali e nessuna modifica alla partita.
|
||||||
// npm run browser:effects — immagini in /tmp/regolo-effects (oppure $OUT).
|
// npm run browser:effects — immagini in /tmp/regolo-effects (oppure $OUT).
|
||||||
import { chromium } from 'playwright';
|
import { chromium } from 'playwright';
|
||||||
import { spawn } from 'node:child_process';
|
import { staticServer } from './helpers.mjs';
|
||||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
const out = process.env.OUT ?? '/tmp/regolo-effects';
|
const out = process.env.OUT ?? '/tmp/regolo-effects';
|
||||||
mkdirSync(out, { recursive: true });
|
mkdirSync(out, { recursive: true });
|
||||||
const port = 9000 + Math.floor(Math.random() * 900);
|
const server = await staticServer(), port = server.port;
|
||||||
const server = spawn('python3', ['-m', 'http.server', String(port), '--bind', '127.0.0.1'], { stdio: 'ignore' });
|
|
||||||
let browser;
|
let browser;
|
||||||
try {
|
try {
|
||||||
browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
||||||
|
|||||||
+19
-3
@@ -2,14 +2,14 @@
|
|||||||
// fino alla fine, salvataggio e impaginazione su telefono. Schermate in $OUT (predefinito: /tmp/regolo-shots).
|
// fino alla fine, salvataggio e impaginazione su telefono. Schermate in $OUT (predefinito: /tmp/regolo-shots).
|
||||||
// npm run browser
|
// npm run browser
|
||||||
import { chromium } from 'playwright';
|
import { chromium } from 'playwright';
|
||||||
import { spawn } from 'node:child_process';
|
import { staticServer } from './helpers.mjs';
|
||||||
import { mkdirSync, readFileSync } from 'node:fs';
|
import { mkdirSync, readFileSync } from 'node:fs';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { solution, route } from '../src/story/solution.js';
|
import { solution, route } from '../src/story/solution.js';
|
||||||
|
|
||||||
const OUT = process.env.OUT ?? '/tmp/regolo-shots', PORT = 8000 + Math.floor(Math.random() * 900);
|
const OUT = process.env.OUT ?? '/tmp/regolo-shots';
|
||||||
mkdirSync(OUT, { recursive: true });
|
mkdirSync(OUT, { recursive: true });
|
||||||
const server = spawn('python3', ['-m', 'http.server', String(PORT), '--bind', '127.0.0.1'], { stdio: 'ignore' });
|
const server = await staticServer(), PORT = server.port;
|
||||||
const browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
const browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
@@ -276,6 +276,22 @@ try {
|
|||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
assert.ok(await page.isHidden('#map'));
|
assert.ok(await page.isHidden('#map'));
|
||||||
assert.equal(await page.evaluate(() => game.state.scene), 'piazza');
|
assert.equal(await page.evaluate(() => game.state.scene), 'piazza');
|
||||||
|
// Con tutti i posti visti e la finestra piccola la mappa si scorre: filtrando da in fondo, i posti trovati si vedono
|
||||||
|
// (prima la mappa restava lunga quanto quella intera, scorsa su un fondo vuoto).
|
||||||
|
await page.setViewportSize({ width: 560, height: 340 });
|
||||||
|
await page.evaluate(() => { for (const id of Object.keys(game.story.scenes)) game.state.flags['visto:' + id] = true; });
|
||||||
|
await page.click('#map-button');
|
||||||
|
assert.ok(await page.evaluate(() => { const s = document.getElementById('map-scroll'); s.scrollTop = s.scrollHeight; return s.scrollTop > 0; }), 'la mappa piena si scorre');
|
||||||
|
await page.fill('#map-search', 'campo');
|
||||||
|
const found = await page.evaluate(() => {
|
||||||
|
const s = document.getElementById('map-scroll'), r = s.getBoundingClientRect();
|
||||||
|
const c = [...document.querySelectorAll('#map-board button')].find(b => !b.hidden && !b.parentElement.hidden).getBoundingClientRect();
|
||||||
|
return { inside: c.top >= r.top && c.bottom <= r.bottom, scrollTop: s.scrollTop, h: s.scrollHeight, ch: s.clientHeight, top: c.top - r.top, q: document.getElementById('map-search').value };
|
||||||
|
});
|
||||||
|
assert.ok(found.inside, `filtrando, il primo posto trovato non si vede: ${JSON.stringify(found)}`);
|
||||||
|
await page.screenshot({ path: `${OUT}/07d-mappa-ricerca-piccola.png` });
|
||||||
|
await page.click('#close-map');
|
||||||
|
await page.setViewportSize({ width: 1280, height: 860 });
|
||||||
|
|
||||||
const phone = await open({ width: 390, height: 844 });
|
const phone = await open({ width: 390, height: 844 });
|
||||||
await phone.click('#splash'); // un clic salta il cartello
|
await phone.click('#splash'); // un clic salta il cartello
|
||||||
|
|||||||
@@ -16,3 +16,16 @@ export function settle(game, { maxSteps = 20000 } = {}) {
|
|||||||
export function memoryStorage() {
|
export function memoryStorage() {
|
||||||
return { data: {}, getItem(k) { return this.data[k] ?? null; }, setItem(k, v) { this.data[k] = String(v); }, removeItem(k) { delete this.data[k]; } };
|
return { data: {}, getItem(k) { return this.data[k] ?? null; }, setItem(k, v) { this.data[k] = String(v); }, removeItem(k) { delete this.data[k]; } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Il server statico delle prove nel browser, su una porta libera scelta dal sistema: con una porta a caso poteva
|
||||||
|
// capitare la 8080 del gioco in Docker (make up), e la prova girava sul codice vecchio del container.
|
||||||
|
export async function staticServer() {
|
||||||
|
const { spawn } = await import('node:child_process');
|
||||||
|
const server = spawn('python3', ['-u', '-m', 'http.server', '0', '--bind', '127.0.0.1'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
||||||
|
const port = await new Promise((resolve, reject) => {
|
||||||
|
let out = '';
|
||||||
|
server.stdout.on('data', d => { out += d; const m = out.match(/port (\d+)/); if (m) resolve(Number(m[1])); });
|
||||||
|
server.on('exit', () => reject(new Error('il server di prova non è partito')));
|
||||||
|
});
|
||||||
|
return { port, kill: () => server.kill() };
|
||||||
|
}
|
||||||
|
|||||||
@@ -470,3 +470,14 @@ test('i quattro rioni: ognuno ha la sua riga nell\'introduzione, e le battute su
|
|||||||
assert.doesNotMatch(look('pieve', 'piazza', 'colonna'), /noi rossi/);
|
assert.doesNotMatch(look('pieve', 'piazza', 'colonna'), /noi rossi/);
|
||||||
assert.match(look(null, 'pieve', 'chiesa'), /quelli in bianco/, 'un salvataggio di prima dei rioni funziona');
|
assert.match(look(null, 'pieve', 'chiesa'), /quelli in bianco/, 'un salvataggio di prima dei rioni funziona');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('gli amici in giro: l\'Arcu al circolino, il Tozzino al cancello del tennis, il BaronDeDuke alla polisportiva', () => {
|
||||||
|
const g = new Game(story);
|
||||||
|
settle(g);
|
||||||
|
const talk = (scene, who) => { g.go(scene); settle(g); g.act('talk', who); return settle(g).map(s => `${s.who ?? 'andrea'}: ${s.text}`); };
|
||||||
|
assert.deepEqual(talk('campomigliaio_circolo', 'arcu'), ['andrea: Arcu! Ma che è sempre là, Borgo?', 'arcu: E chi lo sposta???']);
|
||||||
|
assert.ok(talk('carza', 'tozzino').includes('tozzino: E chi sennò? Ha chiamato ora: è imbottigliato in Bolognese.'));
|
||||||
|
const baron = talk('polisportiva', 'barondeduke');
|
||||||
|
assert.ok(baron.includes('barondeduke: Avevo due donne e due jack. Entra la terza donna, e penso: «Male male si divide».'));
|
||||||
|
assert.equal(baron.at(-1), 'barondeduke: L\'altro aveva colore.');
|
||||||
|
});
|
||||||
|
|||||||
+4
-1
@@ -6,5 +6,8 @@
|
|||||||
"castello": { "speaker": "Damien Black", "filtro": "asetrate=24000*0.9,aresample=24000,atempo=0.95,aecho=0.8:0.7:90|180:0.25|0.15" },
|
"castello": { "speaker": "Damien Black", "filtro": "asetrate=24000*0.9,aresample=24000,atempo=0.95,aecho=0.8:0.7:90|180:0.25|0.15" },
|
||||||
"leone": { "speaker": "Baldur Sanjin", "filtro": "asetrate=24000*0.84,aresample=24000,atempo=1.08" },
|
"leone": { "speaker": "Baldur Sanjin", "filtro": "asetrate=24000*0.84,aresample=24000,atempo=1.08" },
|
||||||
"san_pietro": { "speaker": "Wulf Carlevaro", "filtro": "asetrate=24000*0.92,aresample=24000,atempo=0.95,aecho=0.8:0.6:60:0.2" },
|
"san_pietro": { "speaker": "Wulf Carlevaro", "filtro": "asetrate=24000*0.92,aresample=24000,atempo=0.95,aecho=0.8:0.6:60:0.2" },
|
||||||
"tosca": { "speaker": "Gitta Nikolina" }
|
"tosca": { "speaker": "Gitta Nikolina" },
|
||||||
|
"arcu": { "speaker": "Luis Moray" },
|
||||||
|
"tozzino": { "speaker": "Xavier Hayasaka", "filtro": "asetrate=24000*1.12,aresample=24000,atempo=0.92" },
|
||||||
|
"barondeduke": { "speaker": "Craig Gutsy" }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user