diff --git a/game/end_to_end_tests/test_level_editor.py b/game/end_to_end_tests/test_level_editor.py index c7da90a7f..fda0bb0c4 100644 --- a/game/end_to_end_tests/test_level_editor.py +++ b/game/end_to_end_tests/test_level_editor.py @@ -289,3 +289,30 @@ def test_electric_fuel_gauge(self): ) electric_fuel_gauge = self.selenium.find_element(By.ID, "electricFuelGauge") assert "visibility: visible" in electric_fuel_gauge.get_attribute("style") + + def test_pigeon(self): + """Test that cows on the map automatically become pigeons when the theme is changed to "city" """ + page = self.go_to_level_editor() + page.go_to_scenery_tab() + + source_cow = self.selenium.find_element(By.ID, "cow") + end_space = self.selenium.find_element( + By.CSS_SELECTOR, "rect[x='130'][y='530']" + ) + ActionChains(self.selenium).drag_and_drop(source_cow, end_space).perform() + + scenery_cow = self.selenium.find_elements( + By.CSS_SELECTOR, "image[x='0'][y='0']" + ) + cow_link = scenery_cow[0].get_attribute("href") + assert cow_link == "/static/game/raphael_image/Clarice.svg" + + Select(self.selenium.find_element(By.ID, "theme_select")).select_by_value( + "city" + ) + + scenery_pigeon = self.selenium.find_elements( + By.CSS_SELECTOR, "image[x='0'][y='0']" + ) + pigeon_link = scenery_pigeon[0].get_attribute("href") + assert pigeon_link == "/static/game/raphael_image/pigeon.svg" diff --git a/game/static/game/image/pigeon.svg b/game/static/game/image/pigeon.svg new file mode 100644 index 000000000..9afd7eb16 --- /dev/null +++ b/game/static/game/image/pigeon.svg @@ -0,0 +1,684 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/game/static/game/js/blockly/msg/js/ca.js b/game/static/game/js/blockly/msg/js/ca.js index 8c21ea6b6..26f030b54 100755 --- a/game/static/game/js/blockly/msg/js/ca.js +++ b/game/static/game/js/blockly/msg/js/ca.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "semàfor verd"; Blockly.Msg["DEAD_END_TITLE"] = "és carrer sense sortida"; Blockly.Msg["AT_DESTINATION_TITLE"] = "a destí"; Blockly.Msg["COW_CROSSING_TITLE"] = "vaques"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "coloms"; Blockly.Msg["CALL_PROC_TITLE"] = "Crida"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "Crida un procediment"; diff --git a/game/static/game/js/blockly/msg/js/en-gb.js b/game/static/game/js/blockly/msg/js/en-gb.js index fb9e42f7f..86fa64048 100755 --- a/game/static/game/js/blockly/msg/js/en-gb.js +++ b/game/static/game/js/blockly/msg/js/en-gb.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "traffic light green"; Blockly.Msg["DEAD_END_TITLE"] = "is dead end"; Blockly.Msg["AT_DESTINATION_TITLE"] = "at destination"; Blockly.Msg["COW_CROSSING_TITLE"] = "cows"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "pigeons"; Blockly.Msg["CALL_PROC_TITLE"] = "Call"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "Calls a procedure"; diff --git a/game/static/game/js/blockly/msg/js/en.js b/game/static/game/js/blockly/msg/js/en.js index 584b42dd6..108f16c82 100755 --- a/game/static/game/js/blockly/msg/js/en.js +++ b/game/static/game/js/blockly/msg/js/en.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "traffic light green"; Blockly.Msg["DEAD_END_TITLE"] = "is dead end"; Blockly.Msg["AT_DESTINATION_TITLE"] = "at destination"; Blockly.Msg["COW_CROSSING_TITLE"] = "cows"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "pigeons"; Blockly.Msg["CALL_PROC_TITLE"] = "Call"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "Calls a procedure"; diff --git a/game/static/game/js/blockly/msg/js/es.js b/game/static/game/js/blockly/msg/js/es.js index 08bf8fc25..a40936c00 100755 --- a/game/static/game/js/blockly/msg/js/es.js +++ b/game/static/game/js/blockly/msg/js/es.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "semáforo en verde"; Blockly.Msg["DEAD_END_TITLE"] = "es un callejón sin salida"; Blockly.Msg["AT_DESTINATION_TITLE"] = "en destino"; Blockly.Msg["COW_CROSSING_TITLE"] = "vacas"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "palomas"; Blockly.Msg["CALL_PROC_TITLE"] = "Llamar"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "Llamar a una función"; diff --git a/game/static/game/js/blockly/msg/js/fr.js b/game/static/game/js/blockly/msg/js/fr.js index decee21fb..e8991aa0f 100755 --- a/game/static/game/js/blockly/msg/js/fr.js +++ b/game/static/game/js/blockly/msg/js/fr.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "feu vert"; Blockly.Msg["DEAD_END_TITLE"] = "cul-de-sac"; Blockly.Msg["AT_DESTINATION_TITLE"] = "arrivé à destination"; Blockly.Msg["COW_CROSSING_TITLE"] = "vaches"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "pigeons"; Blockly.Msg["CALL_PROC_TITLE"] = "Appeler"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "Appeler une fonction"; diff --git a/game/static/game/js/blockly/msg/js/hi.js b/game/static/game/js/blockly/msg/js/hi.js index 5555386a3..9d998dc39 100755 --- a/game/static/game/js/blockly/msg/js/hi.js +++ b/game/static/game/js/blockly/msg/js/hi.js @@ -461,6 +461,7 @@ Blockly.Msg["TRAFFIC_LIGHT_GREEN_TITLE"] = "ट्रैफिक लाइट Blockly.Msg["DEAD_END_TITLE"] = "आगे का रास्ता बंद है"; Blockly.Msg["AT_DESTINATION_TITLE"] = "मंजिल /गंतव्य पर"; Blockly.Msg["COW_CROSSING_TITLE"] = "गायों"; +Blockly.Msg["PIGEON_CROSSING_TITLE"] = "कबूतरों"; Blockly.Msg["CALL_PROC_TITLE"] = "पुकारना"; Blockly.Msg["CALL_PROC_TOOLTIP"] = "एक प्रक्रिया बुलाना"; diff --git a/game/static/game/js/blocklyCustomBlocks.js b/game/static/game/js/blocklyCustomBlocks.js index 12ffec32a..2ca5c950f 100644 --- a/game/static/game/js/blocklyCustomBlocks.js +++ b/game/static/game/js/blocklyCustomBlocks.js @@ -281,13 +281,26 @@ function initCustomBlocksDescription() { init: function() { this.setColour(210); this.setOutput(true, 'Boolean'); + let imageUrl = ocargo.Drawing.animalType == ocargo.Cow.PIGEON ? ocargo.Drawing.pigeonUrl : ocargo.Drawing.whiteCowUrl this.appendDummyInput() - .appendField(Blockly.Msg.COW_CROSSING_TITLE) - .appendField(new Blockly.FieldImage(ocargo.Drawing.imageDir + ocargo.Drawing.whiteCowUrl, + .appendField(ocargo.Drawing.animalType == ocargo.Cow.PIGEON ? Blockly.Msg.PIGEON_CROSSING_TITLE: Blockly.Msg.COW_CROSSING_TITLE) + .appendField(new Blockly.FieldImage(ocargo.Drawing.imageDir + imageUrl, ocargo.BlocklyControl.COW_WIDTH, ocargo.BlocklyControl.BLOCK_HEIGHT), 'IMAGE'); } }; + + Blockly.Blocks['pigeon_crossing_IMAGE_ONLY'] = { + init: function() { + this.setColour(210); + this.setOutput(true, 'Boolean'); + this.appendDummyInput() + .appendField("pigeons") + .appendField(new Blockly.FieldImage(ocargo.Drawing.imageDir + ocargo.Drawing.pigeonUrl, + ocargo.BlocklyControl.COW_WIDTH, + ocargo.BlocklyControl.BLOCK_HEIGHT), 'IMAGE'); + } + } /****************/ /* Procedures */ /****************/ diff --git a/game/static/game/js/cow.js b/game/static/game/js/cow.js index 1f0b1e2f0..03eedb410 100644 --- a/game/static/game/js/cow.js +++ b/game/static/game/js/cow.js @@ -26,14 +26,14 @@ ocargo.Cow.prototype.reset = function() { ocargo.Cow.prototype.queueLeaveAnimation = function(model, node) { ocargo.animation.appendAnimation({ type: 'callable', - functionCall: ocargo.sound.cow, - description: 'cow sound' + functionCall: this.type == ocargo.Cow.PIGEON ? ocargo.sound.pigeon : ocargo.sound.cow, + description: 'animal sound' }); ocargo.animation.appendAnimation({ type: 'cow_leave', id: this.id, coordinate: node.coordinate, - description: 'Cow leaving' + description: 'animal leaving' }); }; @@ -49,3 +49,4 @@ ocargo.Cow.ACTIVE = 'ACTIVE'; ocargo.Cow.INACTIVE = 'INACTIVE'; ocargo.Cow.WHITE = 'WHITE'; ocargo.Cow.BROWN = 'BROWN'; +ocargo.Cow.PIGEON = 'PIGEON'; diff --git a/game/static/game/js/drawing.js b/game/static/game/js/drawing.js index ecd965d2c..c7b4ddcd6 100644 --- a/game/static/game/js/drawing.js +++ b/game/static/game/js/drawing.js @@ -1236,11 +1236,14 @@ ocargo.Drawing.renderCoins = function (coins) { } ocargo.Drawing.cowUrl = function (type) { + ocargo.Drawing.animalType = type; switch (type) { case ocargo.Cow.WHITE: return ocargo.Drawing.whiteCowUrl case ocargo.Cow.BROWN: return ocargo.Drawing.brownCowUrl + case ocargo.Cow.PIGEON: + return ocargo.Drawing.pigeonUrl default: return ocargo.Drawing.whiteCowUrl } @@ -1284,6 +1287,9 @@ ocargo.Drawing.TOP_VIEW = 'top_view' ocargo.Drawing.whiteCowUrl = 'Clarice.svg' ocargo.Drawing.brownCowUrl = 'Clarice_Jersey.svg' +ocargo.Drawing.pigeonUrl = 'pigeon.svg' ocargo.Drawing.imageDir = '/static/game/image/' ocargo.Drawing.raphaelImageDir = '/static/game/raphael_image/' + +ocargo.Drawing.animalType = 'WHITE' \ No newline at end of file diff --git a/game/static/game/js/level_editor.js b/game/static/game/js/level_editor.js index 83e7d9327..05f022af6 100644 --- a/game/static/game/js/level_editor.js +++ b/game/static/game/js/level_editor.js @@ -415,7 +415,8 @@ ocargo.LevelEditor = function(levelId) { for (var i = 0; i < BLOCKS.length; i++) { var type = BLOCKS[i]; - var block = Blockly.mainWorkspace.newBlock(type); + let usePigeons = type === "cow_crossing" && currentTheme == THEMES.city + var block = usePigeons ? Blockly.mainWorkspace.newBlock("pigeon_crossing_IMAGE_ONLY") : Blockly.mainWorkspace.newBlock(type); block.initSvg(); block.render(); @@ -909,7 +910,7 @@ ocargo.LevelEditor = function(levelId) { var color = COW_GROUP_COLOR_PALETTE[(currentCowGroupId - 1) % COW_GROUP_COLOR_PALETTE.length]; var style = 'background-color: ' + color; var value = 'group' + currentCowGroupId++; - var type = ocargo.Cow.WHITE; + var type = currentTheme == THEMES.city ? ocargo.Cow.PIGEON : ocargo.Cow.WHITE; cowGroups[value] = { id: value, @@ -2374,6 +2375,7 @@ ocargo.LevelEditor = function(levelId) { function setTheme(theme) { currentTheme = theme; + let newType = currentTheme == THEMES.city ? ocargo.Cow.PIGEON : ocargo.Cow.WHITE; for (var x = 0; x < GRID_WIDTH; x++) { for (var y = 0; y < GRID_HEIGHT; y++) { @@ -2397,6 +2399,36 @@ ocargo.LevelEditor = function(levelId) { }); $('#paper').css({'background-color': theme.background}); + + const animalSource = theme == THEMES.city ? "/static/game/image/pigeon.svg" : "/static/game/image/Clarice.svg"; + + $('#cow').each(function(index, element) { + element.src = animalSource; + }) + + $('#animals_label').each(function(index, element) { + element.innerHTML = theme == THEMES.city ? "Pigeons" : "Cows"; + }) + + for (let [key, value] of Object.entries(cowGroups)) { + value["type"] = theme == THEMES.city ? ocargo.Cow.PIGEON : ocargo.Cow.WHITE; + } + + for (let i = 0; i < cows.length; i++) { + cows[i].updateTheme(); + } + + const pigeonHTML = `pigeons`; + + const cowHTML = `cows`; + + $("#cow_crossing_image").html(newType == ocargo.Cow.PIGEON ? pigeonHTML : cowHTML); } function sortNodes(nodes) { @@ -2554,35 +2586,6 @@ ocargo.LevelEditor = function(levelId) { new InternalTrafficLight(trafficLightData[i]); } - if(COW_LEVELS_ENABLED) { - var cowGroupData = JSON.parse(state.cows); - for (var i = 0; i < cowGroupData.length; i++) { - // Add new group to group select element - if (i >= Object.keys(cowGroups).length) { - addCowGroup(); - } - var cowGroupId = Object.keys(cowGroups)[i]; - cowGroups[cowGroupId].minCows = cowGroupData[i].minCows; - cowGroups[cowGroupId].maxCows = cowGroupData[i].maxCows; - cowGroups[cowGroupId].type = cowGroupData[i].type; - - if (cowGroupData[i].potentialCoordinates != null) { - for (var j = 0; j < cowGroupData[i].potentialCoordinates.length; j++) { - var cowData = { - coordinates: [cowGroupData[i].potentialCoordinates[j]], - group: cowGroups[cowGroupId] - }; - new InternalCow(cowData); - } - } - } - - // Trigger change listener on cow group select box to set initial min/max values - $('#cow_group_select').change(); - - markCowNodes(); - } - // Load in destination and origin nodes if (state.destinations) { var houses = JSON.parse(state.destinations); @@ -2630,6 +2633,36 @@ ocargo.LevelEditor = function(levelId) { PAPER_HEIGHT - currentTheme.decor[decor[i].decorName].height - decor[i].y + PAPER_PADDING); } + //Load in cow data + if(COW_LEVELS_ENABLED) { + var cowGroupData = JSON.parse(state.cows); + for (var i = 0; i < cowGroupData.length; i++) { + // Add new group to group select element + if (i >= Object.keys(cowGroups).length) { + addCowGroup(); + } + var cowGroupId = Object.keys(cowGroups)[i]; + cowGroups[cowGroupId].minCows = cowGroupData[i].minCows; + cowGroups[cowGroupId].maxCows = cowGroupData[i].maxCows; + cowGroups[cowGroupId].type = cowGroupData[i].type; + + if (cowGroupData[i].potentialCoordinates != null) { + for (var j = 0; j < cowGroupData[i].potentialCoordinates.length; j++) { + var cowData = { + coordinates: [cowGroupData[i].potentialCoordinates[j]], + group: cowGroups[cowGroupId] + }; + new InternalCow(cowData); + } + } + } + + // Trigger change listener on cow group select box to set initial min/max values + $('#cow_group_select').change(); + + markCowNodes(); + } + // Load in block data if(state.blocks) { for(var i = 0; i < BLOCKS.length; i++) { @@ -2918,6 +2951,31 @@ ocargo.LevelEditor = function(levelId) { }; + this.updateTheme = function() { + let newType = currentTheme == THEMES.city ? ocargo.Cow.PIGEON : ocargo.Cow.WHITE; + let transformDimensions = this["image"]["_"]["transform"][0] + let rotateDimensions = this["image"]["_"]["transform"][1] + let x = transformDimensions[1] + let y = transformDimensions[2] + let r = 0; + if (rotateDimensions) { + r = rotateDimensions[1]; + } + + this.image.remove(); + + this.image = drawing.createCowImage(newType); + if (this.controlledNode !== null) { + let controlledNode = this.controlledNode; + let coordinates = controlledNode.coordinate; + drawing.setCowImagePosition(coordinates, this.image, controlledNode); + } else { + this.image.transform("t" + x + "," + y + " r" + r); + } + + setupCowListeners(this); + } + this.image = drawing.createCowImage(data.group.type); this.valid = false; diff --git a/game/static/game/js/sound.js b/game/static/game/js/sound.js index e9f57fe18..afad8fb40 100644 --- a/game/static/game/js/sound.js +++ b/game/static/game/js/sound.js @@ -43,6 +43,10 @@ ocargo.sound.cowSound = new Howl({ urls: ['/static/game/sound/cow.mp3', '/static/game/sound/cow.ogg'] }); +ocargo.sound.pigeonSound = new Howl({ + urls: ['/static/game/sound/pigeon.mp3', '/static/game/sound/pigeon.ogg'] +}); + function safePlay(sound) { try { sound.play(); @@ -96,6 +100,10 @@ ocargo.sound.cow = function() { safePlay(ocargo.sound.cowSound); }; +ocargo.sound.pigeon = function() { + safePlay(ocargo.sound.pigeonSound); +} + ocargo.sound.sound_horn = function() { safePlay(ocargo.sound.hornSound); }; diff --git a/game/static/game/raphael_image/pigeon.svg b/game/static/game/raphael_image/pigeon.svg new file mode 100644 index 000000000..9988526b6 --- /dev/null +++ b/game/static/game/raphael_image/pigeon.svg @@ -0,0 +1,685 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/game/static/game/sound/pigeon.mp3 b/game/static/game/sound/pigeon.mp3 new file mode 100644 index 000000000..b968e4475 Binary files /dev/null and b/game/static/game/sound/pigeon.mp3 differ diff --git a/game/static/game/sound/pigeon.ogg b/game/static/game/sound/pigeon.ogg new file mode 100644 index 000000000..c8507fc5a Binary files /dev/null and b/game/static/game/sound/pigeon.ogg differ diff --git a/game/templates/game/level_editor.html b/game/templates/game/level_editor.html index 5ba7c09c5..f2a36eca7 100644 --- a/game/templates/game/level_editor.html +++ b/game/templates/game/level_editor.html @@ -364,7 +364,7 @@