diff --git a/truthseeker/__init__.py b/truthseeker/__init__.py index 87a0a1d..dca9bcb 100644 --- a/truthseeker/__init__.py +++ b/truthseeker/__init__.py @@ -23,7 +23,7 @@ class TruthSeekerApp(flask.Flask): self.config["SECRET_KEY"] = os.getenv("FLASK_SECRET") - self.socketio_app = SocketIO(self) + self.socketio_app = SocketIO(self,cors_allowed_origins=["https://truthinquiry.simailadjalim.fr","http://127.0.0.1:5000"]) self.discord_bot = discord_bot.DiscordBot() token = os.getenv("DISCORD_BOT_TOKEN") diff --git a/truthseeker/logic/data_persistance/data_access.py b/truthseeker/logic/data_persistance/data_access.py index 4d76116..bffe2b6 100644 --- a/truthseeker/logic/data_persistance/data_access.py +++ b/truthseeker/logic/data_persistance/data_access.py @@ -47,6 +47,10 @@ def get_trait_from_trait_id(trait_id): trait = session.query(tables.Trait).filter_by(TRAIT_ID=trait_id).one() return trait +def get_reaction_description(lang,npc_id,trait_id): + desc_lid = session.query(tables.Reaction).filter_by(NPC_ID=npc_id,TRAIT_ID=trait_id).one().DESC_LID + return get_text_from_lid(lang,desc_lid) + def get_traits(lang): traits = [] for trait in session.query(tables.Trait).all(): diff --git a/truthseeker/logic/game_logic.py b/truthseeker/logic/game_logic.py index 9e6f2f8..350e557 100644 --- a/truthseeker/logic/game_logic.py +++ b/truthseeker/logic/game_logic.py @@ -78,6 +78,7 @@ class Game: traitId = self.reaction_table[npc_id] trait = get_trait_from_trait_id(traitId) npcs[npc_id]["reaction"] = get_text_from_lid("FR",trait.NAME_LID) + npcs[npc_id]["description"] = get_reaction_description("FR",npc_id,trait.TRAIT_ID) player_results = data["player"] = {} for member in self.members: player_results[member.username] = member.results @@ -89,6 +90,7 @@ class Game: """ #TODO Get language from player self.gamedata, self.reaction_table = generate_game_data("FR") + self.gamedata["game_id"] = self.game_id def get_member(self, username: str) -> Union[Member, None]: """ @@ -114,13 +116,13 @@ class Game: self.members.append(member) return member - def get_npc_reaction(self, npc_id, reaction) -> None: + def get_npc_reaction(self, npc_id) -> None: """ TODO + TODO TYPES """ if npc_id not in self.reaction_table.keys(): return 0 - reaction_id = self.reaction_table[npc_id][int(reaction)] + reaction_id = self.reaction_table[npc_id] return read_image(f"./truthseeker/static/images/npc/{npc_id}/{reaction_id}.png") def get_player_results(self, responses: dict) -> None: @@ -131,7 +133,7 @@ class Game: try: for npc_id in responses: trait_id = get_trait_id_from_string(responses[npc_id]) - results[npc_id] = trait_id == str(self.reaction_table[npc_id]) + results[npc_id] = trait_id == self.reaction_table[npc_id] return results except: return False diff --git a/truthseeker/routes/routes_api.py b/truthseeker/routes/routes_api.py index 96167a4..25ff771 100644 --- a/truthseeker/routes/routes_api.py +++ b/truthseeker/routes/routes_api.py @@ -120,8 +120,11 @@ def get_data(): @routes_api.route("/getNpcImage", methods=["GET", "POST"]) def getNpcImage(): npc_id = flask.request.values.get("npcid") + if npc_id is None: + return {"error": 1, "msg": "no npc was given"} image = game_logic.get_npc_image(npc_id) - + if image is None: + return {"error": 1, "msg": "npc not found"} response = flask.make_response(image) response.headers.set('Content-Type', 'image/png') response.headers.set( @@ -137,9 +140,8 @@ def getNpcReaction(): if game == None: return {"error": 1, "msg": "this game doesn't exist"} npc_id = flask.request.values.get("npcid") - reactionid = flask.request.values.get("reactionid") - image = game.get_npc_reaction(npc_id,reactionid) + image = game.get_npc_reaction(npc_id) errors = ["npc not in game","error reading file"] if image in [0,1]: return {"error" :1, "msg": errors[image]} , 500 @@ -147,7 +149,7 @@ def getNpcReaction(): response = flask.make_response(image) response.headers.set('Content-Type', 'image/png') response.headers.set( - 'Content-Disposition', 'attachment', filename=f'{reactionid}.png') + 'Content-Disposition', 'attachment', filename=f'reaction.png') return response @routes_api.route("/gameProgress", methods=["GET", "POST"]) @@ -194,6 +196,7 @@ def checkAnwser(): if game.has_finished(): jsonGameResults = game.generate_game_results() APP.socketio_app.emit("gamefinshed",jsonGameResults,room="game."+game.game_id) + #TODO desctruct game response = {"error": 0} return response diff --git a/truthseeker/static/js/api.js b/truthseeker/static/js/api.js index 803dd3f..3ea92c4 100644 --- a/truthseeker/static/js/api.js +++ b/truthseeker/static/js/api.js @@ -15,3 +15,14 @@ async function makeAPIRequest(endpoint, body){ }) }) } +async function makeAPIImageRequest(endpoint, body){ + return new Promise((resolve, reject)=>{ + const fetchOptions = { + method: "POST", + body: new URLSearchParams(body) + } + fetch("/api/v1/"+endpoint, fetchOptions).then(resp => { + resolve(resp) + }) + }) +} diff --git a/truthseeker/static/js/game.js b/truthseeker/static/js/game.js new file mode 100644 index 0000000..56691c5 --- /dev/null +++ b/truthseeker/static/js/game.js @@ -0,0 +1,246 @@ +var npcs_ids = [] +var gamedata = {} +var currentNpc = null +var score = null + +function show(className){ + document.getElementsByClassName(className)[0].classList.remove("hidden"); +} + +function hide(className){ + document.getElementsByClassName(className)[0].classList.add("hidden"); +} + +function setListenerToIntroductionNextBtn(){ + document.getElementById("introduction_next_btn").addEventListener("click", showInterogationViewFromIntroduction); +} + +function setListenerToInterrogationSuspectPreviousBtn(){ + document.getElementById("interrogation_suspect_previous_btn").addEventListener("click",goBackToInterogation) +} + +function setListenerToInterrogationNextBtn(){ + document.getElementById("interrogation_next_btn").addEventListener("click", showEmotionAndCulpritChoicesView) +} + +function setQuestionButtonsListeners(){ + document.getElementById("QA_0").addEventListener("click",askTypeZeroQuestion); + document.getElementById("QA_1").addEventListener("click",askTypeOneQuestion); +} + +function goBackToInterogation(){ + hide("interrogation_suspect"); + show("interrogation"); +} + + +function showInterogationViewFromIntroduction(){ + hide("introduction"); + show("interrogation"); +} + +function showEmotionAndCulpritChoicesView(){ + hide("interrogation"); + show("emotion_and_culprit_choices"); +} + +function getNpcLocationAndPartner(npcid){ + data = {} + npcid = parseInt(npcid) + for(const room in gamedata["rooms"]){ + if(gamedata["rooms"][room]["npcs"].includes(npcid)){ + data["room"] = gamedata["rooms"][room]["name"]; + if(gamedata["rooms"][room]["npcs"].length === 1){ + do{ + const random = Math.floor(Math.random() * npcs_ids.length); + data["partner"] = npcs_ids[random] + }while(data["partner"] === npcid); + } + else{ + data["partner"] = gamedata["rooms"][room]["npcs"][gamedata["rooms"][room]["npcs"][1] === npcid ?0:1]; + } + } + } + return data; +} + + +function getCulprit(){ + culprit = null + Object.values(gamedata["rooms"]).forEach(element =>{ + if (element['npcs'].length === 1){ + culprit = element['npcs'][0]; + return; + } + }) + return culprit +} + +async function askTypeOneQuestion(){ + partnerId = getNpcLocationAndPartner(currentNpc)["partner"]; + anwser = gamedata["npcs"][currentNpc]["QA_1"]; + anwser = anwser.replace("{NPC}",gamedata["npcs"][partnerId]["name"]); + document.getElementsByClassName("suspect_answer")[0].textContent = anwser; + show("question_answer"); + document.getElementById("currentNpcPicure").src = "/api/v1//getNpcReaction?npcid="+currentNpc; + //sleep for 5 sec + await new Promise(r => setTimeout(r, 5000)); + document.getElementById("currentNpcPicure").src = "/api/v1/getNpcImage?npcid="+currentNpc; + hide("question_answer"); + document.getElementsByClassName("suspect_answer")[0].textContent = ""; +} + + +async function askTypeZeroQuestion(){ + room = getNpcLocationAndPartner(currentNpc)["room"]; + anwser = gamedata["npcs"][currentNpc]["QA_0"]; + anwser = anwser.replace("{SALLE}",room); + document.getElementsByClassName("suspect_answer")[0].textContent = anwser; + show("question_answer"); + document.getElementById("currentNpcPicure").src = "/api/v1//getNpcReaction?npcid="+currentNpc; + //sleep for 5 sec + await new Promise(r => setTimeout(r, 5000)); + document.getElementById("currentNpcPicure").src = "/api/v1/getNpcImage?npcid="+currentNpc; + hide("question_answer"); + document.getElementsByClassName("suspect_answer")[0].textContent = ""; +} + +async function sendAnswers(){ + selects = document.getElementsByClassName("suspect_emotion_chooser"); + let playerResponses = {} + for (let index = 0; index < selects.length; index++) { + select = selects[index]; + playerResponses[select.id] = select.value + } + data = {}; + data["responses"] = JSON.stringify(playerResponses); + return await makeAPIRequest("submitAnswers",data); +} + +function renderAnswerSelectionPanel() { + npcs_ids.forEach(element => { + let suspect = document.createElement("div"); + suspect.classList.add("suspect"); + + suspect_emotion_chooser = document.createElement("select"); + suspect_emotion_chooser.classList.add("suspect_emotion_chooser") + suspect_emotion_chooser.setAttribute("id",element); + gamedata["traits"].forEach(trait =>{ + let option = document.createElement("option"); + option.value = trait; + option.text = trait; + suspect_emotion_chooser.appendChild(option); + }); + suspect.appendChild(suspect_emotion_chooser); + let data = {}; + let img = document.createElement('img'); + img.classList.add("suspect_picture"); + img.src = "/api/v1/getNpcImage?npcid="+element; + suspect.appendChild(img); + let button = document.getElementById("culpritButton"); + let button_clone = button.cloneNode(true); + button_clone.addEventListener("click",()=>{ + sendAnswers(); + }); + button_clone.removeAttribute("id"); + button_clone.classList.remove("hidden"); + suspect.appendChild(button_clone); + document.getElementById("culprits_choices").appendChild(suspect); + }); +} + +function renderInterogation(){ + document.getElementById("QA_0").textContent = gamedata["questions"]["QA_0"], + document.getElementById("QA_1").textContent = gamedata["questions"]["QA_1"], + npcs_ids.forEach(element => { + let suspect = document.createElement("div"); + suspect.classList.add("suspect"); + + let img = document.createElement('img'); + img.classList.add("suspect_picture"); + img.src = "/api/v1/getNpcImage?npcid="+element; + suspect.appendChild(img); + let button = document.getElementById("interogationButton"); + let button_clone = button.cloneNode(true); + button_clone.classList.remove("hidden"); + button_clone.addEventListener("click",()=>{ + currentNpc = element + document.getElementById("currentNpcPicure").src = "/api/v1/getNpcImage?npcid="+element; + hide("interrogation"); + show("interrogation_suspect"); + }) + suspect.appendChild(button_clone) + document.getElementById("interrogation_suspects").appendChild(suspect); + }); +} + +function initSock(){ + socket = io({ + auth:{ + game_id: gamedata["game_id"] + } + }); + + socket.on("connect", () => { + console.log("Connected !") + }) + + socket.on("gameprogress", (username) => { + console.log(username); + }); + + socket.on("gamefinshed", (finalResults) => { + hide("emotion_and_culprit_choices"); + console.log(finalResults); + for (const player in finalResults["player"]){ + let playerNode = document.createElement("h3") + playerNode.classList.add("player_name_and_score") + let playerResultArray = Object.values(finalResults["player"][player]) + playerNode.textContent = "" + player + " : " + playerResultArray.filter(x => x==true).length + document.getElementsByClassName("players_list")[0].appendChild(playerNode); + } + culprit = getCulprit(); + document.getElementsByClassName("reveal_culprit_title")[0].textContent += " " + gamedata["npcs"][culprit]["name"]; + document.getElementById("culprit").src = "/api/v1/getNpcImage?npcid="+culprit; + show("results_game"); + npcs_ids.filter(x => x!=culprit).forEach(npcid =>{ + let suspect = document.createElement("div"); + suspect.classList.add("summary_suspect"); + let img = document.createElement("img") + img.src = "/api/v1/getNpcImage?npcid=" + npcid; + suspect.appendChild(img) + + let emotionTitle = document.createElement("h2"); + emotionTitle.classList.add("explain_suspect_emotion_title"); + emotionTitle.textContent = "Ce suspect était " + finalResults["npcs"][npcid]["reaction"]; + suspect.appendChild(emotionTitle); + + let emotionDesc = document.createElement("p"); + emotionDesc.classList.add("explain_suspect_emotion_description"); + emotionDesc.textContent = "Qui se caractérise par un " + finalResults["npcs"][npcid]["description"]; + suspect.appendChild(emotionDesc) + + document.getElementsByClassName("suspects_list")[0].appendChild(suspect) + }) + }); +} + +async function setGameData(){ + data = {}; + response = await makeAPIRequest("getGameData"); + gamedata = response["gamedata"]; + npcs_ids = Object.keys(gamedata["npcs"]); +} + +async function initGame(){ + await setGameData(); + initSock(); + renderAnswerSelectionPanel(); + renderInterogation(); + setQuestionButtonsListeners() + setListenerToInterrogationSuspectPreviousBtn() + setListenerToIntroductionNextBtn() + setListenerToInterrogationNextBtn(); + show("introduction"); +} +initGame(); \ No newline at end of file diff --git a/truthseeker/static/js/game_start_page.js b/truthseeker/static/js/game_start_page.js index a57470d..11d58a0 100644 --- a/truthseeker/static/js/game_start_page.js +++ b/truthseeker/static/js/game_start_page.js @@ -108,16 +108,6 @@ function areInputsValid(checkRoomCode) { return true; } -function startSoloGame() { - if (!areInputsValid(false)) { - return; - } - - hideInvalidInputErrorMessage(); - - //TODO: code to start solo game -} - function createMultiPlayerRoom() { if (!areInputsValid(false)) { return; @@ -209,6 +199,13 @@ function changeTheme() { } async function startSoloGame(){ + + if (!areInputsValid(false)) { + return; + } + + hideInvalidInputErrorMessage(); + username = document.getElementById("game_username").value; let data = {} data["username"] = username; diff --git a/truthseeker/templates/game.html b/truthseeker/templates/game.html index 2d0eac5..3b3eaeb 100644 --- a/truthseeker/templates/game.html +++ b/truthseeker/templates/game.html @@ -26,12 +26,7 @@ -
- -
- Example - -
+

Débrief

-
- Example -

Ce suspect était ...

-

En effet, la ... se caractérise par un ...

-
-
- Example -

Ce suspect était ...

-

En effet, la ... se caractérise par un ...

-
-
- Example -

Ce suspect était ...

-

En effet, la ... se caractérise par un ...

-
-
- Example -

Ce suspect était ...

-

En effet, la ... se caractérise par un ...

-
+
@@ -148,6 +98,22 @@
+ + + + + + + +