2017-09-07 18:38:18 +02:00
// swad_game.c: games using remote control
2017-07-09 20:31:40 +02:00
/*
SWAD ( Shared Workspace At a Distance ) ,
is a web platform developed at the University of Granada ( Spain ) ,
and used to support university teaching .
This file is part of SWAD core .
2019-01-07 21:52:19 +01:00
Copyright ( C ) 1999 - 2019 Antonio Ca <EFBFBD> as Vargas
2017-07-09 20:31:40 +02:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation , either version 3 of the
License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
2018-10-22 09:04:08 +02:00
# define _GNU_SOURCE // For asprintf
2017-07-09 20:31:40 +02:00
# include <linux/limits.h> // For PATH_MAX
# include <linux/stddef.h> // For NULL
2018-10-22 09:04:08 +02:00
# include <stdio.h> // For asprintf
2017-07-09 20:31:40 +02:00
# include <stdlib.h> // For calloc
# include <string.h> // For string functions
# include "swad_alert.h"
# include "swad_box.h"
# include "swad_database.h"
2018-11-09 20:47:39 +01:00
# include "swad_form.h"
2017-09-07 18:38:18 +02:00
# include "swad_game.h"
2017-07-09 20:31:40 +02:00
# include "swad_global.h"
# include "swad_group.h"
# include "swad_pagination.h"
# include "swad_parameter.h"
# include "swad_role.h"
2019-03-26 11:53:21 +01:00
# include "swad_setting.h"
2017-07-09 20:31:40 +02:00
# include "swad_table.h"
2017-07-16 13:54:11 +02:00
# include "swad_test.h"
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl ;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
# define Gam_MAX_CHARS_ANSWER (1024 - 1) // 1023
# define Gam_MAX_BYTES_ANSWER ((Gam_MAX_CHARS_ANSWER + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 16383
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
# define Gam_MAX_BYTES_LIST_ANSWER_TYPES (10 + (Gam_NUM_ANS_TYPES - 1) * (1 + 10))
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
const char * Gam_StrAnswerTypesDB [ Gam_NUM_ANS_TYPES ] =
2017-07-09 20:31:40 +02:00
{
" unique_choice " ,
" multiple_choice " ,
} ;
2017-09-07 18:38:18 +02:00
# define Gam_MAX_ANSWERS_PER_QUESTION 10
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
# define Gam_MAX_SELECTED_QUESTIONS 1000
# define Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS (Gam_MAX_SELECTED_QUESTIONS * (1 + 10 + 1))
2017-07-18 20:34:32 +02:00
2019-05-28 15:06:53 +02:00
# define Gam_NEW_MATCH_SECTION_ID "new_match"
2019-08-01 18:48:38 +02:00
# define Gam_AFTER_LAST_QUESTION ((unsigned)((1UL << 31) - 1)) // 2^31 - 1, don't change this number because it is used in database to indicate that a match is finished
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
struct Match
{
long MchCod ;
long GamCod ;
long UsrCod ;
time_t TimeUTC [ 2 ] ;
char Title [ Gam_MAX_BYTES_TITLE + 1 ] ;
2019-05-29 01:14:56 +02:00
struct
{
2019-05-30 12:57:31 +02:00
unsigned QstInd ; // 0 means that the game has not started. First question has index 0.
2019-05-29 01:14:56 +02:00
long QstCod ;
time_t QstStartTimeUTC ;
2019-05-30 20:52:23 +02:00
bool ShowingAnswers ;
2019-07-17 18:17:44 +02:00
bool BeingPlayed ;
2019-07-25 10:45:36 +02:00
unsigned NumPlayers ;
2019-05-29 01:14:56 +02:00
} Status ;
2019-05-28 15:06:53 +02:00
} ;
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
long Gam_CurrentGamCod = - 1L ; // Used as parameter in contextual links
long Gam_CurrentMchCod = - 1L ; // Used as parameter in contextual links
2019-05-30 12:57:31 +02:00
unsigned Gam_CurrentQstInd = 0 ; // Used as parameter in contextual links
2019-05-20 08:52:07 +02:00
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_ListAllGames ( void ) ;
2019-07-04 19:45:15 +02:00
static bool Gam_CheckIfICanEditGames ( void ) ;
2017-09-07 18:38:18 +02:00
static void Gam_PutIconsListGames ( void ) ;
static void Gam_PutIconToCreateNewGame ( void ) ;
static void Gam_PutButtonToCreateNewGame ( void ) ;
static void Gam_PutParamsToCreateNewGame ( void ) ;
2017-09-13 16:24:29 +02:00
static void Gam_ShowOneGame ( long GamCod ,
bool ShowOnlyThisGame ,
bool ListGameQuestions ,
2019-07-04 19:45:15 +02:00
bool PutFormNewMatch ) ;
2017-09-07 18:38:18 +02:00
static void Gam_WriteAuthor ( struct Game * Game ) ;
2017-07-09 20:31:40 +02:00
2019-04-20 22:40:57 +02:00
static void Gam_PutFormsToRemEditOneGame ( const struct Game * Game ,
2019-05-20 08:52:07 +02:00
const char * Anchor ) ;
2017-09-07 18:38:18 +02:00
static void Gam_PutParams ( void ) ;
2019-05-28 15:06:53 +02:00
static void Gam_PutParamCurrentMchCod ( void ) ;
2019-07-04 17:17:15 +02:00
static void Gam_PutHiddenParamOrder ( void ) ;
static void Gam_GetParamOrder ( void ) ;
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
static void Gam_GetGameTxtFromDB ( long GamCod , char Txt [ Cns_MAX_BYTES_TEXT + 1 ] ) ;
2017-07-09 20:31:40 +02:00
2019-05-31 10:25:20 +02:00
static void Gam_PutParamMatchCod ( long MchCod ) ;
static long Gam_GetParamMatchCod ( void ) ;
2017-09-07 18:38:18 +02:00
static bool Gam_CheckIfSimilarGameExists ( struct Game * Game ) ;
2019-07-17 11:31:42 +02:00
static void Gam_ShowLstGrpsToCreateMatch ( void ) ;
2017-09-13 16:24:29 +02:00
2017-09-07 18:38:18 +02:00
static void Gam_CreateGame ( struct Game * Game , const char * Txt ) ;
static void Gam_UpdateGame ( struct Game * Game , const char * Txt ) ;
2019-05-28 15:06:53 +02:00
static void Gam_CreateGrps ( long MchCod ) ;
static void Gam_GetAndWriteNamesOfGrpsAssociatedToMatch ( struct Match * Match ) ;
static bool Gam_CheckIfIPlayThisMatchBasedOnGrps ( long GamCod ) ;
2017-09-07 18:38:18 +02:00
static unsigned Gam_GetNumQstsGame ( long GamCod ) ;
2017-09-13 21:22:52 +02:00
static void Gam_PutParamQstInd ( unsigned QstInd ) ;
static unsigned Gam_GetParamQstInd ( void ) ;
2019-07-15 12:41:23 +02:00
static void Gam_PutParamAnswer ( unsigned AnsInd ) ;
static unsigned Gam_GetParamAnswer ( void ) ;
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetQstIndFromStr ( const char * UnsignedStr ) ;
static void Gam_RemAnswersOfAQuestion ( long GamCod , unsigned QstInd ) ;
2017-09-07 18:38:18 +02:00
2017-09-11 19:06:46 +02:00
static long Gam_GetQstCodFromQstInd ( long GamCod , unsigned QstInd ) ;
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetMaxQuestionIndexInGame ( long GamCod ) ;
static unsigned Gam_GetPrevQuestionIndexInGame ( long GamCod , unsigned QstInd ) ;
static unsigned Gam_GetNextQuestionIndexInGame ( long GamCod , unsigned QstInd ) ;
2017-09-07 18:38:18 +02:00
static void Gam_ListGameQuestions ( struct Game * Game ) ;
2019-05-28 15:06:53 +02:00
static void Gam_ListOneOrMoreQuestionsForEdition ( long GamCod , unsigned NumQsts ,
2017-09-06 15:03:43 +02:00
MYSQL_RES * mysql_res ) ;
2017-09-07 18:38:18 +02:00
static void Gam_PutIconToAddNewQuestions ( void ) ;
static void Gam_PutButtonToAddNewQuestions ( void ) ;
2017-07-18 20:34:32 +02:00
2017-09-07 18:38:18 +02:00
static void Gam_AllocateListSelectedQuestions ( void ) ;
static void Gam_FreeListsSelectedQuestions ( void ) ;
static unsigned Gam_CountNumQuestionsInList ( void ) ;
2017-07-18 20:34:32 +02:00
2019-07-19 13:11:59 +02:00
static unsigned Gam_GetNumAnswerers ( struct Match * Match ) ;
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetNumUsrsWhoAnswered ( long GamCod , unsigned QstInd , unsigned AnsInd ) ;
2017-09-07 18:38:18 +02:00
static void Gam_DrawBarNumUsrs ( unsigned NumUsrs , unsigned MaxUsrs ) ;
2017-07-09 20:31:40 +02:00
2017-09-08 01:18:20 +02:00
static void Gam_PutParamsOneQst ( void ) ;
2017-07-09 20:31:40 +02:00
2017-09-11 19:06:46 +02:00
static void Gam_ExchangeQuestions ( long GamCod ,
unsigned QstIndTop , unsigned QstIndBottom ) ;
2019-07-04 19:45:15 +02:00
static void Gam_ListMatches ( struct Game * Game , bool PutFormNewMatch ) ;
2019-05-28 15:06:53 +02:00
static void Gam_PutIconToPlayNewMatch ( void ) ;
2019-07-04 19:45:15 +02:00
static void Gam_ListOneOrMoreMatches ( struct Game * Game ,
2019-07-09 22:44:41 +02:00
unsigned NumMatches ,
MYSQL_RES * mysql_res ) ;
2019-05-28 15:06:53 +02:00
static void Gam_GetMatchDataFromRow ( MYSQL_RES * mysql_res ,
struct Match * Match ) ;
static void Gam_PutButtonNewMatch ( long GamCod ) ;
2019-07-04 19:45:15 +02:00
static void Gam_PutFormNewMatch ( struct Game * Game ) ;
2017-09-14 02:32:36 +02:00
2019-07-16 22:33:28 +02:00
static long Gam_CreateMatch ( long GamCod , char Title [ Gam_MAX_BYTES_TITLE + 1 ] ) ;
2019-07-18 22:10:54 +02:00
static void Gam_UpdateMatchStatusInDB ( struct Match * Match ) ;
2019-05-20 19:47:17 +02:00
2019-07-30 15:21:29 +02:00
static void Gam_SetMatchStatusToPrevQuestion ( struct Match * Match ) ;
2019-07-17 22:45:20 +02:00
static void Gam_SetMatchStatusToNextQuestion ( struct Match * Match ) ;
2019-07-04 21:39:30 +02:00
static void Gam_ShowMatchStatusForTch ( struct Match * Match ) ;
2019-07-18 22:48:27 +02:00
static void Gam_ShowMatchStatusForStd ( struct Match * Match ) ;
2019-07-25 10:45:36 +02:00
static void Gam_ShowLeftColumnTch ( struct Match * Match ) ;
static void Gam_ShowLeftColumnStd ( struct Match * Match ) ;
2019-07-31 15:31:02 +02:00
static void Gam_ShowNumPlayers ( struct Match * Match ) ;
static void Gam_ShowMatchTitleAndCloseButton ( struct Match * Match ) ;
2019-07-18 15:16:36 +02:00
static void Gam_ShowQuestionAndAnswersTch ( struct Match * Match ) ;
2019-07-18 22:48:27 +02:00
static void Gam_ShowQuestionAndAnswersStd ( struct Match * Match ) ;
2019-07-17 11:31:42 +02:00
static void Gam_PutBigButton ( Act_Action_t NextAction , long MchCod ,
const char * Icon , const char * Txt ) ;
2019-07-18 22:10:54 +02:00
static void Gam_PutBigButtonClose ( void ) ;
2017-09-13 16:24:29 +02:00
2019-07-16 22:33:28 +02:00
static void Gam_RemoveOldPlayers ( void ) ;
2019-07-17 18:17:44 +02:00
static void Gam_UpdateMatchAsBeingPlayed ( long MchCod ) ;
static void Gam_SetMatchAsNotBeingPlayed ( long MchCod ) ;
static bool Gam_GetIfMatchIsBeingPlayed ( long MchCod ) ;
2019-07-16 22:33:28 +02:00
static void Gam_RegisterMeAsPlayerInMatch ( long MchCod ) ;
2019-07-25 10:45:36 +02:00
static void Gam_GetNumPlayers ( struct Match * Match ) ;
2019-07-16 22:33:28 +02:00
2019-07-04 21:39:30 +02:00
static void Gam_ShowMatchStatusForStd ( struct Match * Match ) ;
2019-07-15 13:43:34 +02:00
static int Gam_GetQstAnsFromDB ( long MchCod , unsigned QstInd ) ;
2019-05-22 09:36:18 +02:00
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
/*************************** List all the games ******************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_SeeAllGames ( void )
2017-07-09 20:31:40 +02:00
{
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Show all the games *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
/**************************** Show all the games *****************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_ListAllGames ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Hlp_ASSESSMENT_Games ;
extern const char * Txt_Games ;
2019-07-04 17:17:15 +02:00
extern const char * Txt_GAMES_ORDER_HELP [ Gam_NUM_ORDERS ] ;
extern const char * Txt_GAMES_ORDER [ Gam_NUM_ORDERS ] ;
2017-07-09 20:31:40 +02:00
extern const char * Txt_No_games ;
2017-09-07 18:38:18 +02:00
Gam_Order_t Order ;
2017-07-09 20:31:40 +02:00
struct Pagination Pagination ;
unsigned NumGame ;
/***** Get number of groups in current course *****/
2019-04-04 10:45:15 +02:00
if ( ! Gbl . Crs . Grps . NumGrps )
Gbl . Crs . Grps . WhichGrps = Grp_ALL_GROUPS ;
2017-07-09 20:31:40 +02:00
/***** Get list of games *****/
2017-09-07 18:38:18 +02:00
Gam_GetListGames ( ) ;
2017-07-09 20:31:40 +02:00
/***** Compute variables related to pagination *****/
Pagination . NumItems = Gbl . Games . Num ;
Pagination . CurrentPage = ( int ) Gbl . Games . CurrentPage ;
Pag_CalculatePagination ( & Pagination ) ;
Gbl . Games . CurrentPage = ( unsigned ) Pagination . CurrentPage ;
/***** Write links to pages *****/
if ( Pagination . MoreThanOnePage )
2017-09-13 16:24:29 +02:00
Pag_WriteLinksToPagesCentered ( Pag_GAMES ,
2017-07-09 20:31:40 +02:00
0 ,
& Pagination ) ;
/***** Start box *****/
2017-09-07 18:38:18 +02:00
Box_StartBox ( " 100% " , Txt_Games , Gam_PutIconsListGames ,
2017-07-09 20:31:40 +02:00
Hlp_ASSESSMENT_Games , Box_NOT_CLOSABLE ) ;
if ( Gbl . Games . Num )
{
/***** Table head *****/
Tbl_StartTableWideMargin ( 2 ) ;
2019-07-04 19:45:15 +02:00
fprintf ( Gbl . F . Out , " <tr> " ) ;
if ( Gam_CheckIfICanEditGames ( ) )
fprintf ( Gbl . F . Out , " <th class= \" CONTEXT_COL \" ></th> " ) ; // Column for contextual icons
2019-07-04 17:17:15 +02:00
for ( Order = ( Gam_Order_t ) 0 ;
Order < = ( Gam_Order_t ) ( Gam_NUM_ORDERS - 1 ) ;
2017-07-09 20:31:40 +02:00
Order + + )
{
fprintf ( Gbl . F . Out , " <th class= \" LEFT_MIDDLE \" > " ) ;
/* Form to change order */
2018-11-09 20:47:39 +01:00
Frm_StartForm ( ActSeeAllGam ) ;
2017-09-13 16:24:29 +02:00
Pag_PutHiddenParamPagNum ( Pag_GAMES , Gbl . Games . CurrentPage ) ;
2017-07-09 20:31:40 +02:00
Par_PutHiddenParamUnsigned ( " Order " , ( unsigned ) Order ) ;
2019-07-04 17:17:15 +02:00
Frm_LinkFormSubmit ( Txt_GAMES_ORDER_HELP [ Order ] , " TIT_TBL " , NULL ) ;
2017-07-09 20:31:40 +02:00
if ( Order = = Gbl . Games . SelectedOrder )
fprintf ( Gbl . F . Out , " <u> " ) ;
2019-07-04 17:17:15 +02:00
fprintf ( Gbl . F . Out , " %s " , Txt_GAMES_ORDER [ Order ] ) ;
2017-07-09 20:31:40 +02:00
if ( Order = = Gbl . Games . SelectedOrder )
fprintf ( Gbl . F . Out , " </u> " ) ;
fprintf ( Gbl . F . Out , " </a> " ) ;
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " </th> " ) ;
}
2019-07-04 17:17:15 +02:00
fprintf ( Gbl . F . Out , " </tr> " ) ;
2017-07-09 20:31:40 +02:00
/***** Write all the games *****/
for ( NumGame = Pagination . FirstItemVisible ;
NumGame < = Pagination . LastItemVisible ;
NumGame + + )
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Gbl . Games . LstGamCods [ NumGame - 1 ] ,
false ,
2019-07-04 19:45:15 +02:00
false , // Do not list game questions
false ) ; // Do not put form to start new match
2017-07-09 20:31:40 +02:00
/***** End table *****/
Tbl_EndTable ( ) ;
}
else // No games created
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_INFO , Txt_No_games ) ;
2017-07-09 20:31:40 +02:00
/***** Button to create a new game *****/
2019-07-04 19:45:15 +02:00
if ( Gam_CheckIfICanEditGames ( ) )
2017-09-07 18:38:18 +02:00
Gam_PutButtonToCreateNewGame ( ) ;
2017-07-09 20:31:40 +02:00
/***** End box *****/
Box_EndBox ( ) ;
/***** Write again links to pages *****/
if ( Pagination . MoreThanOnePage )
2017-09-13 16:24:29 +02:00
Pag_WriteLinksToPagesCentered ( Pag_GAMES ,
2017-07-09 20:31:40 +02:00
0 ,
& Pagination ) ;
/***** Free list of games *****/
2017-09-07 18:38:18 +02:00
Gam_FreeListGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************* Check if I can create a new game **********************/
/*****************************************************************************/
2019-07-04 19:45:15 +02:00
static bool Gam_CheckIfICanEditGames ( void )
2017-07-09 20:31:40 +02:00
{
switch ( Gbl . Usrs . Me . Role . Logged )
{
case Rol_TCH :
case Rol_DEG_ADM :
case Rol_CTR_ADM :
case Rol_INS_ADM :
case Rol_SYS_ADM :
return true ;
default :
return false ;
}
return false ;
}
/*****************************************************************************/
/***************** Put contextual icons in list of games *******************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutIconsListGames ( void )
2017-07-09 20:31:40 +02:00
{
/***** Put icon to create a new game *****/
2019-07-04 19:45:15 +02:00
if ( Gam_CheckIfICanEditGames ( ) )
2017-09-07 18:38:18 +02:00
Gam_PutIconToCreateNewGame ( ) ;
2017-07-09 20:31:40 +02:00
/***** Put icon to show a figure *****/
2019-02-12 14:46:14 +01:00
Gbl . Figures . FigureType = Fig_GAMES ;
Fig_PutIconToShowFigure ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/********************** Put icon to create a new game **********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutIconToCreateNewGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_New_game ;
2019-01-10 15:26:33 +01:00
Ico_PutContextualIconToAdd ( ActFrmNewGam , NULL , Gam_PutParamsToCreateNewGame ,
Txt_New_game ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/********************* Put button to create a new game *********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutButtonToCreateNewGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_New_game ;
2018-11-09 20:47:39 +01:00
Frm_StartForm ( ActFrmNewGam ) ;
2017-09-07 18:38:18 +02:00
Gam_PutParamsToCreateNewGame ( ) ;
2017-07-09 20:31:40 +02:00
Btn_PutConfirmButton ( Txt_New_game ) ;
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************* Put parameters to create a new game *******************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutParamsToCreateNewGame ( void )
2017-07-09 20:31:40 +02:00
{
2017-09-07 18:38:18 +02:00
Gam_PutHiddenParamGameOrder ( ) ;
2017-09-13 16:24:29 +02:00
Pag_PutHiddenParamPagNum ( Pag_GAMES , Gbl . Games . CurrentPage ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/****************************** Show one game ******************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_SeeOneGame ( void )
2017-07-09 20:31:40 +02:00
{
struct Game Game ;
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Show game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
2019-07-04 19:45:15 +02:00
false , // Do not list game questions
false ) ; // Do not put form to start new match
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-13 16:24:29 +02:00
/******************************* Show one game *******************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-13 16:24:29 +02:00
static void Gam_ShowOneGame ( long GamCod ,
bool ShowOnlyThisGame ,
bool ListGameQuestions ,
2019-07-04 19:45:15 +02:00
bool PutFormNewMatch )
2017-07-09 20:31:40 +02:00
{
extern const char * Hlp_ASSESSMENT_Games ;
extern const char * Txt_Game ;
extern const char * Txt_Today ;
extern const char * Txt_View_game ;
extern const char * Txt_No_of_questions ;
2019-05-28 15:06:53 +02:00
extern const char * Txt_New_match ;
2019-04-20 22:40:57 +02:00
char * Anchor = NULL ;
2017-07-09 20:31:40 +02:00
static unsigned UniqueId = 0 ;
struct Game Game ;
char Txt [ Cns_MAX_BYTES_TEXT + 1 ] ;
/***** Start box *****/
2017-09-13 16:24:29 +02:00
if ( ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
Box_StartBox ( NULL , Txt_Game , NULL ,
Hlp_ASSESSMENT_Games , Box_NOT_CLOSABLE ) ;
/***** Get data of this game *****/
Game . GamCod = GamCod ;
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game . GamCod ; // Used as parameter in contextual links
2017-07-09 20:31:40 +02:00
2019-04-20 22:40:57 +02:00
/***** Set anchor string *****/
Frm_SetAnchorStr ( Game . GamCod , & Anchor ) ;
2017-07-09 20:31:40 +02:00
/***** Start table *****/
2017-09-13 16:24:29 +02:00
if ( ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
Tbl_StartTableWide ( 2 ) ;
2019-05-20 08:52:07 +02:00
/***** Start first row of this game *****/
fprintf ( Gbl . F . Out , " <tr> " ) ;
/***** Icons related to this game *****/
2017-07-09 20:31:40 +02:00
if ( Game . Status . ICanEdit )
2019-07-04 19:45:15 +02:00
{
fprintf ( Gbl . F . Out , " <td rowspan= \" 2 \" class= \" CONTEXT_COL " ) ;
if ( ! ShowOnlyThisGame )
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " \" > " ) ;
2019-05-20 08:52:07 +02:00
/* Icons to remove/edit this game */
Gam_PutFormsToRemEditOneGame ( & Game , Anchor ) ;
2019-07-04 19:45:15 +02:00
if ( ShowOnlyThisGame )
/* Icon to start a new match */
2019-07-17 11:31:42 +02:00
Lay_PutContextualLinkOnlyIcon ( ActReqNewMchTch , Gam_NEW_MATCH_SECTION_ID ,
2019-05-28 15:06:53 +02:00
Gam_PutParams ,
" play.svg " ,
2019-07-04 19:45:15 +02:00
Txt_New_match ) ;
2019-05-20 08:52:07 +02:00
2019-07-04 19:45:15 +02:00
fprintf ( Gbl . F . Out , " </td> " ) ;
}
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
/***** Start date/time *****/
2017-07-09 20:31:40 +02:00
UniqueId + + ;
fprintf ( Gbl . F . Out , " <td id= \" gam_date_start_%u \" class= \" %s LEFT_TOP " ,
UniqueId ,
2019-08-01 18:48:38 +02:00
Game . Status . Visible ? " DATE_GREEN " :
" DATE_GREEN_LIGHT " ) ;
2017-09-13 16:24:29 +02:00
if ( ! ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
2019-07-04 16:06:31 +02:00
fprintf ( Gbl . F . Out , " \" > " ) ;
if ( Game . TimeUTC [ Gam_START_TIME ] )
fprintf ( Gbl . F . Out , " <script type= \" text/javascript \" > "
" writeLocalDateHMSFromUTC('gam_date_start_%u',%ld, "
" %u,'<br />','%s',true,true,0x7); "
" </script> " ,
UniqueId , Game . TimeUTC [ Gam_START_TIME ] ,
( unsigned ) Gbl . Prefs . DateFormat , Txt_Today ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
/***** End date/time *****/
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " <td id= \" gam_date_end_%u \" class= \" %s LEFT_TOP " ,
UniqueId ,
2019-08-01 18:48:38 +02:00
Game . Status . Visible ? " DATE_GREEN " :
" DATE_GREEN_LIGHT " ) ;
2017-09-13 16:24:29 +02:00
if ( ! ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
2019-07-04 16:06:31 +02:00
fprintf ( Gbl . F . Out , " \" > " ) ;
if ( Game . TimeUTC [ Gam_END_TIME ] )
fprintf ( Gbl . F . Out , " <script type= \" text/javascript \" > "
" writeLocalDateHMSFromUTC('gam_date_end_%u',%ld, "
" %u,'<br />','%s',false,true,0x7); "
" </script> " ,
UniqueId , Game . TimeUTC [ Gam_END_TIME ] ,
( unsigned ) Gbl . Prefs . DateFormat , Txt_Today ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
/***** Game title and main data *****/
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP " ) ;
2017-09-13 16:24:29 +02:00
if ( ! ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " \" > " ) ;
2019-05-20 08:52:07 +02:00
/* Game title */
2019-04-20 22:40:57 +02:00
Lay_StartArticle ( Anchor ) ;
2019-05-20 08:52:07 +02:00
Frm_StartForm ( ActSeeGam ) ;
2017-09-07 18:38:18 +02:00
Gam_PutParamGameCod ( GamCod ) ;
2018-11-09 20:47:39 +01:00
Frm_LinkFormSubmit ( Txt_View_game ,
2017-07-09 20:31:40 +02:00
Game . Status . Visible ? " ASG_TITLE " :
2017-09-07 18:38:18 +02:00
" ASG_TITLE_LIGHT " , NULL ) ;
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " %s</a> " ,
Game . Title ) ;
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2019-04-20 22:40:57 +02:00
Lay_EndArticle ( ) ;
2017-07-09 20:31:40 +02:00
2019-07-17 19:39:35 +02:00
/* Number of questions */
fprintf ( Gbl . F . Out , " <div class= \" %s \" >%s: %u</div> " ,
2017-07-09 20:31:40 +02:00
Game . Status . Visible ? " ASG_GRP " :
2017-09-13 16:24:29 +02:00
" ASG_GRP_LIGHT " ,
2017-07-09 20:31:40 +02:00
Txt_No_of_questions ,
2019-07-17 19:39:35 +02:00
Game . NumQsts ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
fprintf ( Gbl . F . Out , " </td> " ) ;
/***** End 1st row of this game *****/
fprintf ( Gbl . F . Out , " </tr> " ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
/***** Start 2nd row of this game *****/
2019-05-20 10:35:57 +02:00
fprintf ( Gbl . F . Out , " <tr> " ) ;
/***** Author of the game *****/
fprintf ( Gbl . F . Out , " <td colspan= \" 2 \" class= \" LEFT_TOP " ) ;
2017-09-13 16:24:29 +02:00
if ( ! ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " \" > " ) ;
2017-09-07 18:38:18 +02:00
Gam_WriteAuthor ( & Game ) ;
2019-05-20 10:35:57 +02:00
fprintf ( Gbl . F . Out , " </td> " ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 10:35:57 +02:00
/***** Text of the game *****/
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP " ) ;
2017-09-13 16:24:29 +02:00
if ( ! ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " COLOR%u " , Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " \" > " ) ;
2017-09-07 18:38:18 +02:00
Gam_GetGameTxtFromDB ( Game . GamCod , Txt ) ;
2017-07-09 20:31:40 +02:00
Str_ChangeFormat ( Str_FROM_HTML , Str_TO_RIGOROUS_HTML ,
Txt , Cns_MAX_BYTES_TEXT , false ) ; // Convert from HTML to rigorous HTML
Str_InsertLinks ( Txt , Cns_MAX_BYTES_TEXT , 60 ) ; // Insert links
2017-10-24 11:26:01 +02:00
fprintf ( Gbl . F . Out , " <div class= \" PAR %s \" >%s</div> "
2019-05-20 10:35:57 +02:00
" </td> " ,
2017-07-09 20:31:40 +02:00
Game . Status . Visible ? " DAT " :
2017-09-13 16:24:29 +02:00
" DAT_LIGHT " ,
2017-07-09 20:31:40 +02:00
Txt ) ;
2019-05-20 10:35:57 +02:00
/***** End 2nd row of this game *****/
fprintf ( Gbl . F . Out , " </tr> " ) ;
2017-09-13 16:24:29 +02:00
/***** End table *****/
if ( ShowOnlyThisGame )
2017-07-09 20:31:40 +02:00
Tbl_EndTable ( ) ;
2017-09-13 16:24:29 +02:00
else
Gbl . RowEvenOdd = 1 - Gbl . RowEvenOdd ;
2019-04-20 22:40:57 +02:00
/***** Free anchor string *****/
Frm_FreeAnchorStr ( Anchor ) ;
2019-05-28 15:06:53 +02:00
if ( ShowOnlyThisGame )
{
2019-07-04 19:45:15 +02:00
/***** List matches *****/
Gam_ListMatches ( & Game , PutFormNewMatch ) ;
2019-05-20 10:35:57 +02:00
2019-05-28 15:06:53 +02:00
/***** Write questions of this game *****/
if ( ListGameQuestions )
Gam_ListGameQuestions ( & Game ) ;
2017-07-09 20:31:40 +02:00
2019-05-28 15:06:53 +02:00
/***** End box *****/
2017-07-09 20:31:40 +02:00
Box_EndBox ( ) ;
2019-05-28 15:06:53 +02:00
}
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/*********************** Write the author of a game ************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_WriteAuthor ( struct Game * Game )
2017-07-09 20:31:40 +02:00
{
Usr_WriteAuthor1Line ( Game - > UsrCod , ! Game - > Status . Visible ) ;
}
/*****************************************************************************/
2017-09-13 21:22:52 +02:00
/****** Put a hidden parameter with the type of order in list of games *******/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_PutHiddenParamGameOrder ( void )
2017-07-09 20:31:40 +02:00
{
Par_PutHiddenParamUnsigned ( " Order " , ( unsigned ) Gbl . Games . SelectedOrder ) ;
}
/*****************************************************************************/
2017-09-01 14:36:25 +02:00
/******************** Put a link (form) to edit one game *********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-04-20 22:40:57 +02:00
static void Gam_PutFormsToRemEditOneGame ( const struct Game * Game ,
2019-05-20 08:52:07 +02:00
const char * Anchor )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Reset ;
2017-09-13 12:25:45 +02:00
/***** Put icon to remove game *****/
2017-09-07 18:38:18 +02:00
Ico_PutContextualIconToRemove ( ActReqRemGam , Gam_PutParams ) ;
2017-07-09 20:31:40 +02:00
2017-09-13 12:25:45 +02:00
/***** Put icon to reset game *****/
2019-01-12 03:00:59 +01:00
Lay_PutContextualLinkOnlyIcon ( ActReqRstGam , NULL , Gam_PutParams ,
" recycle.svg " ,
Txt_Reset ) ;
2017-07-09 20:31:40 +02:00
2017-09-13 12:25:45 +02:00
/***** Put icon to hide/show game *****/
2019-04-20 22:40:57 +02:00
if ( Game - > Status . Visible )
Ico_PutContextualIconToHide ( ActHidGam , Anchor , Gam_PutParams ) ;
2017-07-09 20:31:40 +02:00
else
2019-04-20 22:40:57 +02:00
Ico_PutContextualIconToUnhide ( ActShoGam , Anchor , Gam_PutParams ) ;
2017-07-09 20:31:40 +02:00
2017-09-13 12:25:45 +02:00
/***** Put icon to edit game *****/
2017-09-07 18:38:18 +02:00
Ico_PutContextualIconToEdit ( ActEdiOneGam , Gam_PutParams ) ;
2017-07-09 20:31:40 +02:00
}
2017-12-20 19:01:49 +01:00
/*****************************************************************************/
/******************** Params used to edit/play a game ************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutParams ( void )
2017-07-09 20:31:40 +02:00
{
2019-05-20 08:52:07 +02:00
if ( Gam_CurrentGamCod > 0 )
Gam_PutParamGameCod ( Gam_CurrentGamCod ) ;
2017-12-20 19:01:49 +01:00
2019-07-04 17:17:15 +02:00
Gam_PutHiddenParamOrder ( ) ;
2017-09-13 16:24:29 +02:00
Pag_PutHiddenParamPagNum ( Pag_GAMES , Gbl . Games . CurrentPage ) ;
2017-07-09 20:31:40 +02:00
}
2019-05-28 15:06:53 +02:00
static void Gam_PutParamCurrentMchCod ( void )
{
if ( Gam_CurrentMchCod > 0 )
Gam_PutParamMatchCod ( Gam_CurrentMchCod ) ;
}
2019-07-04 17:17:15 +02:00
/*****************************************************************************/
/****** Put a hidden parameter with the type of order in list of games *******/
/*****************************************************************************/
static void Gam_PutHiddenParamOrder ( void )
{
Par_PutHiddenParamUnsigned ( " Order " , ( unsigned ) Gbl . Games . SelectedOrder ) ;
}
/*****************************************************************************/
/********** Get parameter with the type or order in list of games ************/
/*****************************************************************************/
static void Gam_GetParamOrder ( void )
{
Gbl . Games . SelectedOrder = ( Gam_Order_t )
Par_GetParToUnsignedLong ( " Order " ,
0 ,
Gam_NUM_ORDERS - 1 ,
( unsigned long ) Gam_ORDER_DEFAULT ) ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
/*********************** Get list of all the games *************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_GetListGames ( void )
2017-07-09 20:31:40 +02:00
{
2019-01-03 15:25:18 +01:00
static const char * OrderBySubQuery [ Gam_NUM_ORDERS ] =
{
2019-07-04 16:06:31 +02:00
" StartTime DESC,EndTime DESC,games.Title DESC " , // Gam_ORDER_BY_START_DATE
" EndTime DESC,StartTime DESC,games.Title DESC " , // Gam_ORDER_BY_END_DATE
2019-07-04 17:17:15 +02:00
" games.Title DESC " , // Gam_ORDER_BY_TITLE
2019-01-03 15:25:18 +01:00
} ;
2017-07-09 20:31:40 +02:00
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2018-10-31 10:19:01 +01:00
unsigned long NumRows = 0 ; // Initialized to avoid warning
2017-07-09 20:31:40 +02:00
unsigned NumGame ;
/***** Free list of games *****/
if ( Gbl . Games . LstIsRead )
2017-09-07 18:38:18 +02:00
Gam_FreeListGames ( ) ;
2017-07-09 20:31:40 +02:00
/***** Get list of games from database *****/
2019-05-20 08:52:07 +02:00
NumRows = DB_QuerySELECT ( & mysql_res , " can not get games " ,
2019-07-04 16:06:31 +02:00
" SELECT games.GamCod, "
" MIN(gam_matches.StartTime) AS StartTime, "
" MAX(gam_matches.EndTime) AS EndTime "
" FROM games "
" LEFT JOIN gam_matches "
" ON games.GamCod=gam_matches.GamCod "
" WHERE games.CrsCod=%ld "
" GROUP BY games.GamCod "
2019-05-20 08:52:07 +02:00
" ORDER BY %s " ,
2019-05-28 15:06:53 +02:00
Gbl . Hierarchy . Crs . CrsCod ,
2019-05-20 08:52:07 +02:00
OrderBySubQuery [ Gbl . Games . SelectedOrder ] ) ;
2017-07-09 20:31:40 +02:00
if ( NumRows ) // Games found...
{
Gbl . Games . Num = ( unsigned ) NumRows ;
/***** Create list of games *****/
if ( ( Gbl . Games . LstGamCods = ( long * ) calloc ( NumRows , sizeof ( long ) ) ) = = NULL )
2018-10-18 20:06:54 +02:00
Lay_NotEnoughMemoryExit ( ) ;
2017-07-09 20:31:40 +02:00
/***** Get the games codes *****/
for ( NumGame = 0 ;
NumGame < Gbl . Games . Num ;
NumGame + + )
{
/* Get next game code */
row = mysql_fetch_row ( mysql_res ) ;
2017-09-11 19:06:46 +02:00
if ( ( Gbl . Games . LstGamCods [ NumGame ] = Str_ConvertStrCodToLongCod ( row [ 0 ] ) ) < = 0 )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Error: wrong game code. " ) ;
}
}
else
Gbl . Games . Num = 0 ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
Gbl . Games . LstIsRead = true ;
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/********************** Get game data using its code *************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_GetDataOfGameByCod ( struct Game * Game )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned long NumRows ;
/***** Get data of game from database *****/
2018-10-31 10:19:01 +01:00
NumRows = DB_QuerySELECT ( & mysql_res , " can not get game data " ,
2019-07-04 16:06:31 +02:00
" SELECT games.GamCod, " // row[0]
" games.Hidden, " // row[1]
" games.UsrCod, " // row[2]
" games.Title " // row[3]
2018-10-31 10:19:01 +01:00
" FROM games "
2019-07-04 16:06:31 +02:00
" LEFT JOIN gam_matches "
" ON games.GamCod=gam_matches.GamCod "
" WHERE games.GamCod=%ld " ,
2018-10-31 10:19:01 +01:00
Game - > GamCod ) ;
2017-07-09 20:31:40 +02:00
if ( NumRows ) // Game found...
{
/* Get row */
row = mysql_fetch_row ( mysql_res ) ;
/* Get code of the game (row[0]) */
Game - > GamCod = Str_ConvertStrCodToLongCod ( row [ 0 ] ) ;
2019-05-20 08:52:07 +02:00
/* Get whether the game is hidden (row[1]) */
Game - > Status . Visible = ( row [ 1 ] [ 0 ] = = ' N ' ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
/* Get author of the game (row[2]) */
Game - > UsrCod = Str_ConvertStrCodToLongCod ( row [ 2 ] ) ;
2017-07-09 20:31:40 +02:00
2019-07-04 16:06:31 +02:00
/* Get the title of the game (row[3]) */
Str_Copy ( Game - > Title , row [ 3 ] ,
2019-05-28 15:06:53 +02:00
Gam_MAX_BYTES_TITLE ) ;
2017-07-09 20:31:40 +02:00
2019-07-17 19:39:35 +02:00
/* Get number of questions */
2017-09-07 18:38:18 +02:00
Game - > NumQsts = Gam_GetNumQstsGame ( Game - > GamCod ) ;
2017-07-09 20:31:40 +02:00
/* Can I view results of the game?
Can I edit game ? */
switch ( Gbl . Usrs . Me . Role . Logged )
{
case Rol_STD :
2019-05-20 08:52:07 +02:00
Game - > Status . ICanViewResults = Game - > NumQsts ! = 0 & &
2019-08-01 18:48:38 +02:00
Game - > Status . Visible ;
2017-07-09 20:31:40 +02:00
Game - > Status . ICanEdit = false ;
break ;
case Rol_NET :
2019-07-04 16:06:31 +02:00
Game - > Status . ICanViewResults = Game - > NumQsts ! = 0 ;
2017-07-09 20:31:40 +02:00
Game - > Status . ICanEdit = false ;
break ;
case Rol_TCH :
case Rol_DEG_ADM :
case Rol_CTR_ADM :
case Rol_INS_ADM :
2019-07-04 16:06:31 +02:00
Game - > Status . ICanViewResults = Game - > NumQsts ! = 0 ;
2019-05-28 15:06:53 +02:00
Game - > Status . ICanEdit = true ;
2017-07-09 20:31:40 +02:00
break ;
case Rol_SYS_ADM :
2019-05-20 08:52:07 +02:00
Game - > Status . ICanViewResults = Game - > NumQsts ! = 0 ;
2017-07-09 20:31:40 +02:00
Game - > Status . ICanEdit = true ;
break ;
default :
Game - > Status . ICanViewResults = false ;
Game - > Status . ICanEdit = false ;
break ;
}
}
else
{
/* Initialize to empty game */
Game - > GamCod = - 1L ;
Game - > UsrCod = - 1L ;
Game - > Title [ 0 ] = ' \0 ' ;
Game - > NumQsts = 0 ;
2019-05-20 08:52:07 +02:00
Game - > Status . Visible = true ;
Game - > Status . ICanViewResults = false ;
Game - > Status . ICanEdit = false ;
2017-07-09 20:31:40 +02:00
}
2019-07-04 16:06:31 +02:00
/* Free structure that stores the query result */
2017-07-09 20:31:40 +02:00
DB_FreeMySQLResult ( & mysql_res ) ;
2019-07-04 16:06:31 +02:00
if ( Game - > GamCod > 0 )
{
/***** Get start and end times from database *****/
NumRows = DB_QuerySELECT ( & mysql_res , " can not get game data " ,
" SELECT UNIX_TIMESTAMP(MIN(StartTime)), " // row[0]
2019-07-09 22:44:41 +02:00
" UNIX_TIMESTAMP(MAX(EndTime)) " // row[1]
2019-07-04 16:06:31 +02:00
" FROM gam_matches "
" WHERE GamCod=%ld " ,
Game - > GamCod ) ;
if ( NumRows )
{
/* Get row */
row = mysql_fetch_row ( mysql_res ) ;
/* Get start date (row[0] holds the start UTC time) */
Game - > TimeUTC [ Gam_START_TIME ] = Dat_GetUNIXTimeFromStr ( row [ 0 ] ) ;
/* Get end date (row[1] holds the end UTC time) */
Game - > TimeUTC [ Gam_END_TIME ] = Dat_GetUNIXTimeFromStr ( row [ 1 ] ) ;
}
/* Free structure that stores the query result */
DB_FreeMySQLResult ( & mysql_res ) ;
}
else
{
Game - > TimeUTC [ Gam_START_TIME ] =
Game - > TimeUTC [ Gam_END_TIME ] = ( time_t ) 0 ;
}
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/***************************** Free list of games ****************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_FreeListGames ( void )
2017-07-09 20:31:40 +02:00
{
if ( Gbl . Games . LstIsRead & & Gbl . Games . LstGamCods )
{
/***** Free memory used by the list of games *****/
free ( ( void * ) Gbl . Games . LstGamCods ) ;
Gbl . Games . LstGamCods = NULL ;
Gbl . Games . Num = 0 ;
Gbl . Games . LstIsRead = false ;
}
}
/*****************************************************************************/
/********************** Get game text from database ************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_GetGameTxtFromDB ( long GamCod , char Txt [ Cns_MAX_BYTES_TEXT + 1 ] )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned long NumRows ;
/***** Get text of game from database *****/
2018-10-31 10:19:01 +01:00
NumRows = DB_QuerySELECT ( & mysql_res , " can not get game text " ,
" SELECT Txt FROM games WHERE GamCod=%ld " ,
GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** The result of the query must have one row or none *****/
if ( NumRows = = 1 )
{
/* Get info text */
row = mysql_fetch_row ( mysql_res ) ;
Str_Copy ( Txt , row [ 0 ] ,
Cns_MAX_BYTES_TEXT ) ;
}
else
Txt [ 0 ] = ' \0 ' ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
if ( NumRows > 1 )
Lay_ShowErrorAndExit ( " Error when getting game text. " ) ;
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/******************** Write parameter with code of game **********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_PutParamGameCod ( long GamCod )
2017-07-09 20:31:40 +02:00
{
Par_PutHiddenParamLong ( " GamCod " , GamCod ) ;
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/********************* Get parameter with code of game ***********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
long Gam_GetParamGameCod ( void )
2017-07-09 20:31:40 +02:00
{
/***** Get code of game *****/
return Par_GetParToLong ( " GamCod " ) ;
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/******************** Write parameter with code of match **********************/
/*****************************************************************************/
2019-05-31 10:25:20 +02:00
static void Gam_PutParamMatchCod ( long MchCod )
2019-05-28 15:06:53 +02:00
{
Par_PutHiddenParamLong ( " MchCod " , MchCod ) ;
}
/*****************************************************************************/
/********************* Get parameter with code of match **********************/
/*****************************************************************************/
2019-05-31 10:25:20 +02:00
static long Gam_GetParamMatchCod ( void )
2019-05-28 15:06:53 +02:00
{
/***** Get code of match *****/
return Par_GetParToLong ( " MchCod " ) ;
}
/*****************************************************************************/
/*************** Ask for confirmation of removing of a game ******************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_AskRemGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Do_you_really_want_to_remove_the_game_X ;
extern const char * Txt_Remove_game ;
struct Game Game ;
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not remove this game. " ) ;
/***** Show question and button to remove game *****/
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game . GamCod ;
2019-02-17 01:14:55 +01:00
Ale_ShowAlertAndButton ( ActRemGam , NULL , NULL , Gam_PutParams ,
Btn_REMOVE_BUTTON , Txt_Remove_game ,
Ale_QUESTION , Txt_Do_you_really_want_to_remove_the_game_X ,
Game . Title ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/******************************* Remove a game *******************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RemoveGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Game_X_removed ;
struct Game Game ;
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not remove this game. " ) ;
/***** Remove all the questions in this game *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove questions of a game " ,
" DELETE FROM gam_questions WHERE GamCod=%ld " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
2019-05-28 15:06:53 +02:00
/***** Remove all the matches in this game *****/
/* Remove groups in matches of the game */
DB_QueryDELETE ( " can not remove the groups associated to matches of a game " ,
" DELETE FROM gam_grp USING gam_grp,gam_matches "
" WHERE gam_matches.GrpCod=%ld "
" AND gam_matches.MchCod=gam_grp.MchCod " ,
Game . GamCod ) ;
/* Remove matches of the game */
DB_QueryDELETE ( " can not remove matches of a game " ,
" DELETE FROM gam_matches WHERE GamCod=%ld " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Remove game *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove game " ,
" DELETE FROM games WHERE GamCod=%ld " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Write message to show the change made *****/
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_Game_X_removed ,
Game . Title ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/***************** Ask for confirmation of reset of a game *****************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_AskResetGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Do_you_really_want_to_reset_the_game_X ;
2019-05-20 08:52:07 +02:00
extern const char * Txt_Reset_game ;
2017-07-09 20:31:40 +02:00
struct Game Game ;
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not reset this game. " ) ;
/***** Ask for confirmation of reset *****/
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game . GamCod ;
Ale_ShowAlertAndButton ( ActRstGam , NULL , NULL ,
Gam_PutParams ,
Btn_REMOVE_BUTTON ,
Txt_Reset_game ,
Ale_QUESTION , Txt_Do_you_really_want_to_reset_the_game_X ,
Game . Title ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************************* Reset a game ******************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_ResetGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Game_X_reset ;
struct Game Game ;
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not reset this game. " ) ;
/***** Reset all the answers in this game *****/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not reset answers of a game " ,
" UPDATE gam_answers,gam_questions "
" SET gam_answers.NumUsrs=0 "
" WHERE gam_questions.GamCod=%ld "
" AND gam_questions.QstCod=gam_answers.QstCod " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Write message to show the change made *****/
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_Game_X_reset ,
Game . Title ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************************** Hide a game ******************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_HideGame ( void )
2017-07-09 20:31:40 +02:00
{
struct Game Game ;
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not hide this game. " ) ;
/***** Hide game *****/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not hide game " ,
" UPDATE games SET Hidden='Y' WHERE GamCod=%ld " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************************** Show a game ******************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_UnhideGame ( void )
2017-07-09 20:31:40 +02:00
{
struct Game Game ;
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get data of the game from database *****/
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not unhide this game. " ) ;
/***** Show game *****/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not show game " ,
" UPDATE games SET Hidden='N' WHERE GamCod=%ld " ,
Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************* Check if the title of a game exists *******************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static bool Gam_CheckIfSimilarGameExists ( struct Game * Game )
2017-07-09 20:31:40 +02:00
{
/***** Get number of games with a field value from database *****/
2018-11-03 20:52:00 +01:00
return ( DB_QueryCOUNT ( " can not get similar games " ,
" SELECT COUNT(*) FROM games "
2019-05-20 08:52:07 +02:00
" WHERE CrsCod=%ld AND Title='%s' "
" AND GamCod<>%ld " ,
Gbl . Hierarchy . Crs . CrsCod , Game - > Title ,
Game - > GamCod ) ! = 0 ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-07-04 19:45:15 +02:00
/*********************** Put a form to create a new game *********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RequestCreatOrEditGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Hlp_ASSESSMENT_Games_new_game ;
extern const char * Hlp_ASSESSMENT_Games_edit_game ;
2019-02-22 21:47:50 +01:00
extern const char * The_ClassFormInBox [ The_NUM_THEMES ] ;
2017-07-09 20:31:40 +02:00
extern const char * Txt_New_game ;
extern const char * Txt_Edit_game ;
extern const char * Txt_Title ;
extern const char * Txt_Description ;
extern const char * Txt_Create_game ;
2019-02-18 18:27:45 +01:00
extern const char * Txt_Save_changes ;
2017-07-09 20:31:40 +02:00
struct Game Game ;
bool ItsANewGame ;
char Txt [ Cns_MAX_BYTES_TEXT + 1 ] ;
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Get the code of the game *****/
2017-09-07 18:38:18 +02:00
ItsANewGame = ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L ) ;
2017-07-09 20:31:40 +02:00
/***** Get from the database the data of the game *****/
if ( ItsANewGame )
{
/***** Put link (form) to create new game *****/
2019-07-04 19:45:15 +02:00
if ( ! Gam_CheckIfICanEditGames ( ) )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " You can not create a new game here. " ) ;
/* Initialize to empty game */
Game . GamCod = - 1L ;
Game . UsrCod = Gbl . Usrs . Me . UsrDat . UsrCod ;
2019-07-04 19:45:15 +02:00
Game . TimeUTC [ Gam_START_TIME ] = ( time_t ) 0 ;
Game . TimeUTC [ Gam_END_TIME ] = ( time_t ) 0 ;
2019-05-20 08:52:07 +02:00
Game . Title [ 0 ] = ' \0 ' ;
Game . NumQsts = 0 ;
Game . Status . Visible = true ;
Game . Status . ICanViewResults = false ;
2017-07-09 20:31:40 +02:00
}
else
{
/* Get data of the game from database */
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & Game ) ;
2017-07-09 20:31:40 +02:00
if ( ! Game . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not update this game. " ) ;
/* Get text of the game from database */
2017-09-07 18:38:18 +02:00
Gam_GetGameTxtFromDB ( Game . GamCod , Txt ) ;
2017-07-09 20:31:40 +02:00
}
/***** Start form *****/
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game . GamCod ;
2018-11-09 20:47:39 +01:00
Frm_StartForm ( ItsANewGame ? ActNewGam :
2017-07-09 20:31:40 +02:00
ActChgGam ) ;
2017-09-07 18:38:18 +02:00
Gam_PutParams ( ) ;
2017-07-09 20:31:40 +02:00
/***** Start box and table *****/
if ( ItsANewGame )
Box_StartBoxTable ( NULL , Txt_New_game , NULL ,
Hlp_ASSESSMENT_Games_new_game , Box_NOT_CLOSABLE , 2 ) ;
else
2017-10-01 11:57:25 +02:00
Box_StartBoxTable ( NULL ,
Game . Title [ 0 ] ? Game . Title :
Txt_Edit_game ,
NULL ,
2017-07-09 20:31:40 +02:00
Hlp_ASSESSMENT_Games_edit_game , Box_NOT_CLOSABLE , 2 ) ;
/***** Game title *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" RIGHT_MIDDLE \" > "
" <label for= \" Title \" class= \" %s \" >%s:</label> "
" </td> "
" <td class= \" LEFT_MIDDLE \" > "
" <input type= \" text \" id= \" Title \" name= \" Title \" "
" size= \" 45 \" maxlength= \" %u \" value= \" %s \" "
" required= \" required \" /> "
" </td> "
" </tr> " ,
2019-02-22 21:47:50 +01:00
The_ClassFormInBox [ Gbl . Prefs . Theme ] ,
2017-07-09 20:31:40 +02:00
Txt_Title ,
2019-05-28 15:06:53 +02:00
Gam_MAX_CHARS_TITLE , Game . Title ) ;
2017-07-09 20:31:40 +02:00
/***** Game text *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" RIGHT_TOP \" > "
" <label for= \" Txt \" class= \" %s \" >%s:</label> "
" </td> "
" <td class= \" LEFT_TOP \" > "
" <textarea id= \" Txt \" name= \" Txt \" "
" cols= \" 60 \" rows= \" 10 \" > " ,
2019-02-22 21:47:50 +01:00
The_ClassFormInBox [ Gbl . Prefs . Theme ] ,
2017-07-09 20:31:40 +02:00
Txt_Description ) ;
if ( ! ItsANewGame )
fprintf ( Gbl . F . Out , " %s " , Txt ) ;
fprintf ( Gbl . F . Out , " </textarea> "
" </td> "
" </tr> " ) ;
/***** End table, send button and end box *****/
if ( ItsANewGame )
Box_EndBoxTableWithButton ( Btn_CREATE_BUTTON , Txt_Create_game ) ;
else
2019-02-18 18:27:45 +01:00
Box_EndBoxTableWithButton ( Btn_CONFIRM_BUTTON , Txt_Save_changes ) ;
2017-07-09 20:31:40 +02:00
/***** End form *****/
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-07-09 20:31:40 +02:00
/***** Show questions of the game ready to be edited *****/
if ( ! ItsANewGame )
2017-09-07 18:38:18 +02:00
Gam_ListGameQuestions ( & Game ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
/***************** Show list of groups to create a new match *****************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
static void Gam_ShowLstGrpsToCreateMatch ( void )
2017-07-09 20:31:40 +02:00
{
2019-02-22 21:47:50 +01:00
extern const char * The_ClassFormInBox [ The_NUM_THEMES ] ;
2017-07-09 20:31:40 +02:00
extern const char * Txt_Groups ;
extern const char * Txt_The_whole_course ;
unsigned NumGrpTyp ;
/***** Get list of groups types and groups in this course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs ( Grp_ONLY_GROUP_TYPES_WITH_GROUPS ) ;
2019-04-04 10:45:15 +02:00
if ( Gbl . Crs . Grps . GrpTypes . Num )
2017-07-09 20:31:40 +02:00
{
/***** Start box and table *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" %s RIGHT_TOP \" > "
" %s: "
" </td> "
" <td class= \" LEFT_TOP \" > " ,
2019-02-22 21:47:50 +01:00
The_ClassFormInBox [ Gbl . Prefs . Theme ] ,
2017-07-09 20:31:40 +02:00
Txt_Groups ) ;
Box_StartBoxTable ( " 95% " , NULL , NULL ,
NULL , Box_NOT_CLOSABLE , 0 ) ;
/***** First row: checkbox to select the whole course *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td colspan= \" 7 \" class= \" DAT LEFT_MIDDLE \" > "
" <label> "
" <input type= \" checkbox \" "
2019-05-31 10:25:20 +02:00
" id= \" WholeCrs \" name= \" WholeCrs \" value= \" Y \" "
" checked= \" checked \" "
" onclick= \" uncheckChildren(this,'GrpCods') \" /> "
2017-07-09 20:31:40 +02:00
" %s %s "
" </label> "
" </td> "
" </tr> " ,
2019-04-04 10:45:15 +02:00
Txt_The_whole_course , Gbl . Hierarchy . Crs . ShrtName ) ;
2017-07-09 20:31:40 +02:00
/***** List the groups for each group type *****/
for ( NumGrpTyp = 0 ;
2019-04-04 10:45:15 +02:00
NumGrpTyp < Gbl . Crs . Grps . GrpTypes . Num ;
2017-07-09 20:31:40 +02:00
NumGrpTyp + + )
2019-04-04 10:45:15 +02:00
if ( Gbl . Crs . Grps . GrpTypes . LstGrpTypes [ NumGrpTyp ] . NumGrps )
2019-05-28 15:06:53 +02:00
Grp_ListGrpsToEditAsgAttSvyMch ( & Gbl . Crs . Grps . GrpTypes . LstGrpTypes [ NumGrpTyp ] ,
- 1L , // -1 means "New match"
Grp_MATCH ) ;
2017-07-09 20:31:40 +02:00
/***** End table and box *****/
Box_EndBoxTable ( ) ;
fprintf ( Gbl . F . Out , " </td> "
" </tr> " ) ;
}
/***** Free list of groups types and groups in this course *****/
Grp_FreeListGrpTypesAndGrps ( ) ;
}
/*****************************************************************************/
/********************* Receive form to create a new game *******************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RecFormGame ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Already_existed_a_game_with_the_title_X ;
extern const char * Txt_You_must_specify_the_title_of_the_game ;
struct Game OldGame ;
struct Game NewGame ;
bool ItsANewGame ;
bool NewGameIsCorrect = true ;
char Txt [ Cns_MAX_BYTES_TEXT + 1 ] ;
/***** Get the code of the game *****/
2017-09-07 18:38:18 +02:00
ItsANewGame = ( ( NewGame . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L ) ;
2017-07-09 20:31:40 +02:00
2019-05-20 08:52:07 +02:00
if ( ! ItsANewGame )
2017-07-09 20:31:40 +02:00
{
/* Get data of the old (current) game from database */
OldGame . GamCod = NewGame . GamCod ;
2017-09-07 18:38:18 +02:00
Gam_GetDataOfGameByCod ( & OldGame ) ;
2017-07-09 20:31:40 +02:00
if ( ! OldGame . Status . ICanEdit )
Lay_ShowErrorAndExit ( " You can not update this game. " ) ;
}
/***** Get game title *****/
2019-05-28 15:06:53 +02:00
Par_GetParToText ( " Title " , NewGame . Title , Gam_MAX_BYTES_TITLE ) ;
2017-07-09 20:31:40 +02:00
/***** Get game text and insert links *****/
Par_GetParToHTML ( " Txt " , Txt , Cns_MAX_BYTES_TEXT ) ; // Store in HTML format (not rigorous)
/***** Check if title is correct *****/
if ( NewGame . Title [ 0 ] ) // If there's a game title
{
/* If title of game was in database... */
2017-09-07 18:38:18 +02:00
if ( Gam_CheckIfSimilarGameExists ( & NewGame ) )
2017-07-09 20:31:40 +02:00
{
NewGameIsCorrect = false ;
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_WARNING , Txt_Already_existed_a_game_with_the_title_X ,
NewGame . Title ) ;
2017-07-09 20:31:40 +02:00
}
}
else // If there is not a game title
{
NewGameIsCorrect = false ;
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_WARNING , Txt_You_must_specify_the_title_of_the_game ) ;
2017-07-09 20:31:40 +02:00
}
/***** Create a new game or update an existing one *****/
if ( NewGameIsCorrect )
{
if ( ItsANewGame )
2017-09-07 18:38:18 +02:00
Gam_CreateGame ( & NewGame , Txt ) ; // Add new game to database
2017-07-09 20:31:40 +02:00
else
2017-09-07 18:38:18 +02:00
Gam_UpdateGame ( & NewGame , Txt ) ;
2017-07-09 20:31:40 +02:00
}
else
2017-09-07 18:38:18 +02:00
Gam_RequestCreatOrEditGame ( ) ;
2017-07-09 20:31:40 +02:00
/***** Show games again *****/
2017-09-07 18:38:18 +02:00
Gam_ListAllGames ( ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-13 09:47:45 +02:00
/**************************** Create a new game ******************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_CreateGame ( struct Game * Game , const char * Txt )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Created_new_game_X ;
/***** Create a new game *****/
2018-11-03 01:45:36 +01:00
Game - > GamCod =
DB_QueryINSERTandReturnCode ( " can not create new game " ,
" INSERT INTO games "
2019-07-04 16:06:31 +02:00
" (CrsCod,Hidden,UsrCod,Title,Txt) "
2018-11-03 01:45:36 +01:00
" VALUES "
2019-07-04 16:06:31 +02:00
" (%ld,'N',%ld,'%s','%s') " ,
2019-05-20 08:52:07 +02:00
Gbl . Hierarchy . Crs . CrsCod ,
2018-11-03 01:45:36 +01:00
Gbl . Usrs . Me . UsrDat . UsrCod ,
Game - > Title ,
Txt ) ;
2017-07-09 20:31:40 +02:00
/***** Write success message *****/
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_Created_new_game_X ,
Game - > Title ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/************************* Update an existing game *************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_UpdateGame ( struct Game * Game , const char * Txt )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_The_game_has_been_modified ;
/***** Update the data of the game *****/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not update game " ,
" UPDATE games "
2019-05-20 08:52:07 +02:00
" SET CrsCod=%ld, "
" Title='%s', "
" Txt='%s' "
2018-11-03 12:16:40 +01:00
" WHERE GamCod=%ld " ,
2019-05-20 08:52:07 +02:00
Gbl . Hierarchy . Crs . CrsCod ,
2018-11-03 12:16:40 +01:00
Game - > Title ,
Txt ,
Game - > GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Write success message *****/
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_The_game_has_been_modified ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/************* Check if a match is associated to a given group ***************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
bool Gam_CheckIfMatchIsAssociatedToGrp ( long MchCod , long GrpCod )
2017-07-09 20:31:40 +02:00
{
2019-05-28 15:06:53 +02:00
/***** Get if a match is associated to a group from database *****/
return ( DB_QueryCOUNT ( " can not check if a match is associated to a group " ,
2018-11-03 20:52:00 +01:00
" SELECT COUNT(*) FROM gam_grp "
2019-05-28 15:06:53 +02:00
" WHERE MchCod=%ld AND GrpCod=%ld " ,
MchCod , GrpCod ) ! = 0 ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/******************** Remove one group from all the games ********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-29 01:14:56 +02:00
// TODO: Check if this function should be called when removing group
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
void Gam_RemoveGroup ( long GrpCod )
2017-07-09 20:31:40 +02:00
{
/***** Remove group from all the games *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove group "
" from the associations between games and groups " ,
" DELETE FROM gam_grp WHERE GrpCod=%ld " ,
GrpCod ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/**************** Remove groups of one type from all the games ***************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-29 01:14:56 +02:00
// TODO: Check if this function should be called when removing group type
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
void Gam_RemoveGroupsOfType ( long GrpTypCod )
2017-07-09 20:31:40 +02:00
{
/***** Remove group from all the games *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove groups of a type "
" from the associations between games and groups " ,
" DELETE FROM gam_grp USING crs_grp,gam_grp "
" WHERE crs_grp.GrpTypCod=%ld "
" AND crs_grp.GrpCod=gam_grp.GrpCod " ,
GrpTypCod ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/******************* Create groups associated to a match *********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
static void Gam_CreateGrps ( long MchCod )
2017-07-09 20:31:40 +02:00
{
unsigned NumGrpSel ;
2019-05-28 15:06:53 +02:00
/***** Create groups associated to the match *****/
2017-07-09 20:31:40 +02:00
for ( NumGrpSel = 0 ;
2019-04-04 10:45:15 +02:00
NumGrpSel < Gbl . Crs . Grps . LstGrpsSel . NumGrps ;
2017-07-09 20:31:40 +02:00
NumGrpSel + + )
/* Create group */
2019-05-28 15:06:53 +02:00
DB_QueryINSERT ( " can not associate a group to a match " ,
2018-11-02 19:37:11 +01:00
" INSERT INTO gam_grp "
2019-05-28 15:06:53 +02:00
" (MchCod,GrpCod) "
2018-11-02 19:37:11 +01:00
" VALUES "
" (%ld,%ld) " ,
2019-05-28 15:06:53 +02:00
MchCod , Gbl . Crs . Grps . LstGrpsSel . GrpCods [ NumGrpSel ] ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/************* Get and write the names of the groups of a match **************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
static void Gam_GetAndWriteNamesOfGrpsAssociatedToMatch ( struct Match * Match )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Group ;
extern const char * Txt_Groups ;
extern const char * Txt_and ;
extern const char * Txt_The_whole_course ;
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned long NumRow ;
unsigned long NumRows ;
2019-05-28 15:06:53 +02:00
/***** Get groups associated to a match from database *****/
NumRows = DB_QuerySELECT ( & mysql_res , " can not get groups of a match " ,
2018-10-31 10:19:01 +01:00
" SELECT crs_grp_types.GrpTypName,crs_grp.GrpName "
" FROM gam_grp,crs_grp,crs_grp_types "
2019-05-28 15:06:53 +02:00
" WHERE gam_grp.MchCod=%ld "
2018-10-31 10:19:01 +01:00
" AND gam_grp.GrpCod=crs_grp.GrpCod "
" AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod "
" ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName " ,
2019-05-28 15:06:53 +02:00
Match - > MchCod ) ;
2017-07-09 20:31:40 +02:00
/***** Write heading *****/
2019-05-28 15:06:53 +02:00
fprintf ( Gbl . F . Out , " <div class= \" ASG_GRP \" >%s: " ,
2017-07-09 20:31:40 +02:00
NumRows = = 1 ? Txt_Group :
Txt_Groups ) ;
/***** Write groups *****/
if ( NumRows ) // Groups found...
{
/* Get and write the group types and names */
for ( NumRow = 0 ;
NumRow < NumRows ;
NumRow + + )
{
/* Get next group */
row = mysql_fetch_row ( mysql_res ) ;
/* Write group type name and group name */
fprintf ( Gbl . F . Out , " %s %s " , row [ 0 ] , row [ 1 ] ) ;
if ( NumRows > = 2 )
{
if ( NumRow = = NumRows - 2 )
fprintf ( Gbl . F . Out , " %s " , Txt_and ) ;
if ( NumRows > = 3 )
if ( NumRow < NumRows - 2 )
fprintf ( Gbl . F . Out , " , " ) ;
}
}
}
else
fprintf ( Gbl . F . Out , " %s %s " ,
2019-04-04 10:45:15 +02:00
Txt_The_whole_course , Gbl . Hierarchy . Crs . ShrtName ) ;
2017-07-09 20:31:40 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/************* Remove all the games of a place on the hierarchy **************/
/************* (country, institution, centre, degree or course) **************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-04-03 20:57:04 +02:00
void Gam_RemoveGames ( Hie_Level_t Scope , long Cod )
2017-07-09 20:31:40 +02:00
{
/***** Remove all the answers in course games *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove answers of games "
" in a place on the hierarchy "
" DELETE FROM gam_answers "
" USING games,gam_questions,gam_answers "
" WHERE games.Scope='%s' AND games.Cod=%ld "
" AND games.GamCod=gam_questions.GamCod "
" AND gam_questions.QstCod=gam_answers.QstCod " ,
2019-04-01 23:15:17 +02:00
Sco_GetDBStrFromScope ( Scope ) , Cod ) ;
2017-07-09 20:31:40 +02:00
/***** Remove all the questions in course games *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove questions of games "
" in a place on the hierarchy " ,
" DELETE FROM gam_questions "
" USING games,gam_questions "
" WHERE games.Scope='%s' AND games.Cod=%ld "
" AND games.GamCod=gam_questions.GamCod " ,
2019-04-01 23:15:17 +02:00
Sco_GetDBStrFromScope ( Scope ) , Cod ) ;
2017-07-09 20:31:40 +02:00
/***** Remove groups *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove all the groups "
" associated to games of a course " ,
" DELETE FROM gam_grp "
" USING games,gam_grp "
" WHERE games.Scope='%s' AND games.Cod=%ld "
" AND games.GamCod=gam_grp.GamCod " ,
2019-04-01 23:15:17 +02:00
Sco_GetDBStrFromScope ( Scope ) , Cod ) ;
2017-07-09 20:31:40 +02:00
/***** Remove course games *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove all the games in a place on the hierarchy " ,
" DELETE FROM games WHERE Scope='%s' AND Cod=%ld " ,
2019-04-01 23:15:17 +02:00
Sco_GetDBStrFromScope ( Scope ) , Cod ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/************ Check if I belong to any of the groups of a match **************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
static bool Gam_CheckIfIPlayThisMatchBasedOnGrps ( long MchCod )
2017-07-09 20:31:40 +02:00
{
2019-05-28 15:06:53 +02:00
/***** Get if I can play a match from database *****/
return ( DB_QueryCOUNT ( " can not check if I can play a match " ,
" SELECT COUNT(*) FROM gam_matches "
" WHERE MchCod=%ld "
" AND (MchCod NOT IN (SELECT MchCod FROM gam_grp) OR "
" MchCod IN (SELECT gam_grp.MchCod FROM gam_grp,crs_grp_usr "
2018-11-03 20:52:00 +01:00
" WHERE crs_grp_usr.UsrCod=%ld "
" AND gam_grp.GrpCod=crs_grp_usr.GrpCod)) " ,
2019-05-28 15:06:53 +02:00
MchCod , Gbl . Usrs . Me . UsrDat . UsrCod ) ! = 0 ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/******************* Get number of questions of a game *********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static unsigned Gam_GetNumQstsGame ( long GamCod )
2017-07-09 20:31:40 +02:00
{
2019-07-19 13:11:59 +02:00
/***** Get nuumber of questions in a game from database *****/
2018-11-03 20:52:00 +01:00
return
( unsigned ) DB_QueryCOUNT ( " can not get number of questions of a game " ,
" SELECT COUNT(*) FROM gam_questions "
" WHERE GamCod=%ld " ,
GamCod ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/*********** Put a form to edit/create a question in game *****************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RequestNewQuestion ( void )
2017-07-09 20:31:40 +02:00
{
2017-09-01 00:52:19 +02:00
struct Game Game ;
2017-07-09 20:31:40 +02:00
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Get other parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-07-09 20:31:40 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-07-09 20:31:40 +02:00
/***** Show form to create a new question in this game *****/
2017-09-01 00:52:19 +02:00
Tst_ShowFormAskSelectTstsForGame ( Game . GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-07-09 20:31:40 +02:00
}
2017-09-13 21:22:52 +02:00
/*****************************************************************************/
/****************** Write parameter with index of question *******************/
/*****************************************************************************/
static void Gam_PutParamQstInd ( unsigned QstInd )
{
Par_PutHiddenParamUnsigned ( " QstInd " , QstInd ) ;
}
/*****************************************************************************/
/******************* Get parameter with index of question ********************/
/*****************************************************************************/
static unsigned Gam_GetParamQstInd ( void )
{
2019-05-30 12:57:31 +02:00
long LongNum ;
2017-09-13 21:22:52 +02:00
2019-05-30 12:57:31 +02:00
LongNum = Par_GetParToLong ( " QstInd " ) ;
if ( LongNum < 0 )
2017-09-13 21:22:52 +02:00
Lay_ShowErrorAndExit ( " Wrong question index. " ) ;
2019-05-30 12:57:31 +02:00
return ( unsigned ) LongNum ;
2017-09-13 21:22:52 +02:00
}
2019-07-15 12:41:23 +02:00
/*****************************************************************************/
/******************* Write parameter with student's answer *******************/
/*****************************************************************************/
static void Gam_PutParamAnswer ( unsigned AnsInd )
{
Par_PutHiddenParamUnsigned ( " Ans " , AnsInd ) ;
}
/*****************************************************************************/
/******************* Get parameter with student's answer *********************/
/*****************************************************************************/
static unsigned Gam_GetParamAnswer ( void )
{
long LongNum ;
LongNum = Par_GetParToLong ( " Ans " ) ;
if ( LongNum < 0 )
Lay_ShowErrorAndExit ( " Wrong answer index. " ) ;
return ( unsigned ) LongNum ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
/******************* Get parameter with index of question ********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetQstIndFromStr ( const char * UnsignedStr )
2017-07-09 20:31:40 +02:00
{
2019-05-30 12:57:31 +02:00
long LongNum ;
LongNum = Str_ConvertStrCodToLongCod ( UnsignedStr ) ;
return ( LongNum > 0 ) ? ( unsigned ) LongNum :
0 ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
/********************** Remove answers of a game question ********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
static void Gam_RemAnswersOfAQuestion ( long GamCod , unsigned QstInd )
2017-07-09 20:31:40 +02:00
{
2019-05-30 12:57:31 +02:00
/***** Remove answers *****/
DB_QueryDELETE ( " can not remove the answers of a question " ,
" DELETE FROM gam_answers "
" WHERE GamCod=%ld AND QstInd=%u " ,
GamCod , QstInd ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-11 19:06:46 +02:00
/************ Get question code given game and index of question *************/
/*****************************************************************************/
static long Gam_GetQstCodFromQstInd ( long GamCod , unsigned QstInd )
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
long QstCod ;
/***** Get question code of thw question to be moved up *****/
2018-10-31 10:19:01 +01:00
if ( ! DB_QuerySELECT ( & mysql_res , " can not get question code " ,
" SELECT QstCod FROM gam_questions "
" WHERE GamCod=%ld AND QstInd=%u " ,
GamCod , QstInd ) )
2017-09-11 19:06:46 +02:00
Lay_ShowErrorAndExit ( " Error: wrong question code. " ) ;
/***** Get question code (row[0]) *****/
row = mysql_fetch_row ( mysql_res ) ;
if ( ( QstCod = Str_ConvertStrCodToLongCod ( row [ 0 ] ) ) < = 0 )
Lay_ShowErrorAndExit ( " Error: wrong question code. " ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return QstCod ;
}
/*****************************************************************************/
/****************** Get maximum question index in a game *********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
// Question index can be 1, 2, 3...
// Return 0 if no questions
2017-07-09 20:31:40 +02:00
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetMaxQuestionIndexInGame ( long GamCod )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2019-05-30 12:57:31 +02:00
unsigned QstInd = 0 ;
2017-07-09 20:31:40 +02:00
2017-09-01 14:36:25 +02:00
/***** Get maximum question index in a game from database *****/
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get last question index " ,
" SELECT MAX(QstInd) FROM gam_questions WHERE GamCod=%ld " ,
GamCod ) ;
2017-07-09 20:31:40 +02:00
row = mysql_fetch_row ( mysql_res ) ;
if ( row [ 0 ] ) // There are questions
2019-05-30 12:57:31 +02:00
if ( sscanf ( row [ 0 ] , " %u " , & QstInd ) ! = 1 )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Error when getting last question index. " ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return QstInd ;
}
2017-09-11 19:06:46 +02:00
/*****************************************************************************/
/*********** Get previous question index to a given index in a game **********/
/*****************************************************************************/
2019-05-30 13:46:57 +02:00
// Input question index can be 1, 2, 3... n-1
// Return question index will be 1, 2, 3... n if previous question exists, or 0 if no previous question
2017-09-11 19:06:46 +02:00
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetPrevQuestionIndexInGame ( long GamCod , unsigned QstInd )
2017-09-11 19:06:46 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2019-05-30 12:57:31 +02:00
unsigned PrevQstInd = 0 ;
2017-09-11 19:06:46 +02:00
/***** Get previous question index in a game from database *****/
// Although indexes are always continuous...
// ...this implementation works even with non continuous indexes
2018-10-31 10:19:01 +01:00
if ( ! DB_QuerySELECT ( & mysql_res , " can not get previous question index " ,
" SELECT MAX(QstInd) FROM gam_questions "
2019-05-30 12:57:31 +02:00
" WHERE GamCod=%ld AND QstInd<%u " ,
2018-10-31 10:19:01 +01:00
GamCod , QstInd ) )
2017-09-11 19:06:46 +02:00
Lay_ShowErrorAndExit ( " Error: previous question index not found. " ) ;
/***** Get previous question index (row[0]) *****/
row = mysql_fetch_row ( mysql_res ) ;
2017-09-14 02:32:36 +02:00
if ( row [ 0 ] )
2019-05-30 12:57:31 +02:00
if ( sscanf ( row [ 0 ] , " %u " , & PrevQstInd ) ! = 1 )
2017-09-14 02:32:36 +02:00
Lay_ShowErrorAndExit ( " Error when getting previous question index. " ) ;
2017-09-11 19:06:46 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return PrevQstInd ;
}
/*****************************************************************************/
/************* Get next question index to a given index in a game ************/
/*****************************************************************************/
2019-05-30 13:46:57 +02:00
// Input question index can be 0, 1, 2, 3... n-1
// Return question index will be 1, 2, 3... n if next question exists, or 0 if no next question
2017-09-11 19:06:46 +02:00
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetNextQuestionIndexInGame ( long GamCod , unsigned QstInd )
2017-09-11 19:06:46 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2019-08-01 18:48:38 +02:00
unsigned NextQstInd = Gam_AFTER_LAST_QUESTION ; // End of questions has been reached
2017-09-11 19:06:46 +02:00
/***** Get next question index in a game from database *****/
// Although indexes are always continuous...
// ...this implementation works even with non continuous indexes
2018-10-31 10:19:01 +01:00
if ( ! DB_QuerySELECT ( & mysql_res , " can not get next question index " ,
" SELECT MIN(QstInd) FROM gam_questions "
2019-05-30 12:57:31 +02:00
" WHERE GamCod=%ld AND QstInd>%u " ,
2018-10-31 10:19:01 +01:00
GamCod , QstInd ) )
2017-09-11 19:06:46 +02:00
Lay_ShowErrorAndExit ( " Error: next question index not found. " ) ;
/***** Get next question index (row[0]) *****/
row = mysql_fetch_row ( mysql_res ) ;
2017-09-14 02:32:36 +02:00
if ( row [ 0 ] )
2019-05-30 12:57:31 +02:00
if ( sscanf ( row [ 0 ] , " %u " , & NextQstInd ) ! = 1 )
2017-09-14 02:32:36 +02:00
Lay_ShowErrorAndExit ( " Error when getting next question index. " ) ;
2017-09-11 19:06:46 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return NextQstInd ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-06 15:03:43 +02:00
/************************ List the questions of a game ***********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_ListGameQuestions ( struct Game * Game )
2017-07-09 20:31:40 +02:00
{
extern const char * Hlp_ASSESSMENT_Games_questions ;
extern const char * Txt_Questions ;
extern const char * Txt_This_game_has_no_questions ;
extern const char * Txt_Done ;
MYSQL_RES * mysql_res ;
unsigned NumQsts ;
bool Editing = ( Gbl . Action . Act = = ActEdiOneGam | |
2017-09-07 12:00:01 +02:00
Gbl . Action . Act = = ActAddOneGamQst ) ;
2017-09-01 14:36:25 +02:00
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions ;
/***** How to show the questions ******/
2019-07-04 16:06:31 +02:00
if ( Editing )
2017-09-01 14:36:25 +02:00
ActionToDoWithQuestions = Tst_SHOW_GAME_RESULT ;
2019-07-04 16:06:31 +02:00
else
ActionToDoWithQuestions = Tst_SHOW_GAME_TO_ANSWER ;
2017-07-09 20:31:40 +02:00
/***** Get data of questions from database *****/
2018-10-31 10:19:01 +01:00
NumQsts = ( unsigned ) DB_QuerySELECT ( & mysql_res , " can not get data of a question " ,
2019-05-30 12:57:31 +02:00
" SELECT gam_questions.QstInd, " // row[0]
" gam_questions.QstCod, " // row[1]
" tst_questions.AnsType, " // row[2]
" tst_questions.Stem, " // row[3]
" tst_questions.Feedback, " // row[4]
" tst_questions.MedCod " // row[5]
2018-10-31 10:19:01 +01:00
" FROM gam_questions,tst_questions "
" WHERE gam_questions.GamCod=%ld "
" AND gam_questions.QstCod=tst_questions.QstCod "
" ORDER BY gam_questions.QstInd " ,
Game - > GamCod ) ;
2017-07-09 20:31:40 +02:00
/***** Start box *****/
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game - > GamCod ;
2017-09-07 18:38:18 +02:00
Box_StartBox ( NULL , Txt_Questions , Game - > Status . ICanEdit ? Gam_PutIconToAddNewQuestions :
2017-07-09 20:31:40 +02:00
NULL ,
Hlp_ASSESSMENT_Games_questions , Box_NOT_CLOSABLE ) ;
if ( NumQsts )
{
2017-09-06 15:03:43 +02:00
/***** Show the table with the questions *****/
2019-05-28 15:06:53 +02:00
Gam_ListOneOrMoreQuestionsForEdition ( Game - > GamCod , NumQsts , mysql_res ) ;
2017-07-09 20:31:40 +02:00
2017-09-01 14:36:25 +02:00
if ( ActionToDoWithQuestions = = Tst_SHOW_GAME_TO_ANSWER )
2017-07-09 20:31:40 +02:00
{
/***** Button to create/modify game *****/
Btn_PutConfirmButton ( Txt_Done ) ;
/***** End form *****/
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-07-09 20:31:40 +02:00
}
}
else // This game has no questions
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_INFO , Txt_This_game_has_no_questions ) ;
2017-07-09 20:31:40 +02:00
if ( Game - > Status . ICanEdit & & // I can edit
( ! NumQsts | | // This game has no questions
Editing ) ) // I am editing
/***** Put button to add a new question in this game *****/
2017-09-07 18:38:18 +02:00
Gam_PutButtonToAddNewQuestions ( ) ;
2017-07-09 20:31:40 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
/***** End box *****/
Box_EndBox ( ) ;
}
2017-09-06 15:03:43 +02:00
/*****************************************************************************/
/********************* List game questions for edition ***********************/
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
static void Gam_ListOneOrMoreQuestionsForEdition ( long GamCod , unsigned NumQsts ,
2017-09-06 15:03:43 +02:00
MYSQL_RES * mysql_res )
{
extern const char * Txt_Questions ;
extern const char * Txt_No_INDEX ;
extern const char * Txt_Code ;
extern const char * Txt_Tags ;
extern const char * Txt_Question ;
2017-09-08 01:18:20 +02:00
extern const char * Txt_Move_up_X ;
extern const char * Txt_Move_down_X ;
2017-09-10 18:58:26 +02:00
extern const char * Txt_Movement_not_allowed ;
2017-09-06 15:03:43 +02:00
extern const char * Txt_TST_STR_ANSWER_TYPES [ Tst_NUM_ANS_TYPES ] ;
unsigned NumQst ;
MYSQL_ROW row ;
2019-05-30 12:57:31 +02:00
unsigned QstInd ;
unsigned MaxQstInd ;
2017-09-07 12:00:01 +02:00
long QstCod ;
2019-05-30 12:57:31 +02:00
char StrQstInd [ 10 + 1 ] ;
/***** Get maximum question index *****/
MaxQstInd = Gam_GetMaxQuestionIndexInGame ( GamCod ) ;
2017-09-06 15:03:43 +02:00
/***** Write the heading *****/
Tbl_StartTableWideMargin ( 2 ) ;
fprintf ( Gbl . F . Out , " <tr> "
" <th></th> "
" <th class= \" CENTER_TOP \" > "
" %s "
" </th> "
" <th class= \" CENTER_TOP \" > "
" %s "
" </th> "
" <th class= \" CENTER_TOP \" > "
" %s "
" </th> "
" <th class= \" CENTER_TOP \" > "
" %s "
" </th> "
" </tr> " ,
Txt_No_INDEX ,
Txt_Code ,
Txt_Tags ,
Txt_Question ) ;
/***** Write rows *****/
2019-05-30 12:57:31 +02:00
for ( NumQst = 0 ;
2017-09-06 15:03:43 +02:00
NumQst < NumQsts ;
2019-05-30 12:57:31 +02:00
NumQst + + )
2017-09-06 15:03:43 +02:00
{
Gbl . RowEvenOdd = NumQst % 2 ;
row = mysql_fetch_row ( mysql_res ) ;
/*
2019-05-30 12:57:31 +02:00
row [ 0 ] QstInd
row [ 1 ] QstCod
row [ 2 ] AnsType
row [ 3 ] Stem
row [ 4 ] Feedback
row [ 5 ] MedCod
2017-09-06 15:03:43 +02:00
*/
/***** Create test question *****/
Tst_QstConstructor ( ) ;
2019-05-30 12:57:31 +02:00
/* Get question index (row[0]) */
QstInd = Gam_GetQstIndFromStr ( row [ 0 ] ) ;
snprintf ( StrQstInd , sizeof ( StrQstInd ) ,
" %u " ,
QstInd ) ;
/* Get question code (row[1]) */
QstCod = Str_ConvertStrCodToLongCod ( row [ 1 ] ) ;
2017-09-06 15:03:43 +02:00
/***** Icons *****/
2019-05-28 15:06:53 +02:00
Gam_CurrentGamCod = GamCod ;
2019-05-30 12:57:31 +02:00
Gam_CurrentQstInd = QstInd ;
2017-09-06 15:03:43 +02:00
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" BT%u \" > " , Gbl . RowEvenOdd ) ;
2017-09-13 12:25:45 +02:00
/* Put icon to remove the question */
2018-11-09 20:47:39 +01:00
Frm_StartForm ( ActReqRemGamQst ) ;
2019-05-28 15:06:53 +02:00
Gam_PutParamGameCod ( GamCod ) ;
2019-05-30 12:57:31 +02:00
Gam_PutParamQstInd ( QstInd ) ;
2017-09-06 15:03:43 +02:00
Ico_PutIconRemove ( ) ;
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-09-06 15:03:43 +02:00
2017-09-11 19:06:46 +02:00
/* Put icon to move up the question */
2019-05-30 12:57:31 +02:00
if ( QstInd > 1 )
2017-09-10 18:58:26 +02:00
{
2018-10-18 02:02:32 +02:00
snprintf ( Gbl . Title , sizeof ( Gbl . Title ) ,
Txt_Move_up_X ,
2019-05-30 12:57:31 +02:00
StrQstInd ) ;
2019-01-12 03:00:59 +01:00
Lay_PutContextualLinkOnlyIcon ( ActUp_GamQst , NULL , Gam_PutParamsOneQst ,
" arrow-up.svg " ,
Gbl . Title ) ;
2017-09-10 18:58:26 +02:00
}
else
2019-01-11 02:55:01 +01:00
Ico_PutIconOff ( " arrow-up.svg " , Txt_Movement_not_allowed ) ;
2017-09-08 01:18:20 +02:00
2017-09-11 19:06:46 +02:00
/* Put icon to move down the question */
2019-05-30 12:57:31 +02:00
if ( QstInd < MaxQstInd )
2017-09-10 18:58:26 +02:00
{
2018-10-18 02:02:32 +02:00
snprintf ( Gbl . Title , sizeof ( Gbl . Title ) ,
Txt_Move_down_X ,
2019-05-30 12:57:31 +02:00
StrQstInd ) ;
2019-01-12 03:00:59 +01:00
Lay_PutContextualLinkOnlyIcon ( ActDwnGamQst , NULL , Gam_PutParamsOneQst ,
" arrow-down.svg " ,
Gbl . Title ) ;
2017-09-10 18:58:26 +02:00
}
else
2019-01-11 02:55:01 +01:00
Ico_PutIconOff ( " arrow-down.svg " , Txt_Movement_not_allowed ) ;
2017-09-08 01:18:20 +02:00
2017-09-11 19:06:46 +02:00
/* Put icon to edit the question */
2017-09-08 01:18:20 +02:00
Gbl . Test . QstCod = QstCod ;
Ico_PutContextualIconToEdit ( ActEdiOneTstQst , Tst_PutParamQstCod ) ;
2017-09-06 15:03:43 +02:00
fprintf ( Gbl . F . Out , " </td> " ) ;
/* Write number of question */
fprintf ( Gbl . F . Out , " <td class= \" RIGHT_TOP COLOR%u \" > "
2017-10-10 18:25:59 +02:00
" <div class= \" BIG_INDEX \" >%s</div> " ,
2017-09-06 15:03:43 +02:00
Gbl . RowEvenOdd ,
2019-05-30 12:57:31 +02:00
StrQstInd ) ;
2017-09-06 15:03:43 +02:00
2019-05-30 12:57:31 +02:00
/* Write answer type (row[2]) */
Gbl . Test . AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp ( row [ 2 ] ) ;
2017-09-06 15:03:43 +02:00
fprintf ( Gbl . F . Out , " <div class= \" DAT_SMALL \" >%s</div> "
" </td> " ,
Txt_TST_STR_ANSWER_TYPES [ Gbl . Test . AnswerType ] ) ;
/* Write question code */
fprintf ( Gbl . F . Out , " <td class= \" DAT_SMALL CENTER_TOP COLOR%u \" > "
" %ld "
" </td> " ,
Gbl . RowEvenOdd , Gbl . Test . QstCod ) ;
/* Write the question tags */
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP COLOR%u \" > " ,
Gbl . RowEvenOdd ) ;
Tst_GetAndWriteTagsQst ( Gbl . Test . QstCod ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
2019-05-30 12:57:31 +02:00
/* Write stem (row[3]) */
2017-09-06 15:03:43 +02:00
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP COLOR%u \" > " ,
Gbl . RowEvenOdd ) ;
2019-05-30 12:57:31 +02:00
Tst_WriteQstStem ( row [ 3 ] , " TEST_EDI " ) ;
2019-03-18 15:42:22 +01:00
2019-05-30 12:57:31 +02:00
/* Get media (row[5]) */
Gbl . Test . Media . MedCod = Str_ConvertStrCodToLongCod ( row [ 5 ] ) ;
2019-03-18 15:42:22 +01:00
Med_GetMediaDataByCod ( & Gbl . Test . Media ) ;
/* Show media */
2019-03-02 21:49:11 +01:00
Med_ShowMedia ( & Gbl . Test . Media ,
2019-03-27 14:36:57 +01:00
" TEST_MED_EDIT_LIST_STEM_CONTAINER " ,
" TEST_MED_EDIT_LIST_STEM " ) ;
2019-03-18 15:42:22 +01:00
2019-05-30 12:57:31 +02:00
/* Show feedback (row[4]) */
Tst_WriteQstFeedback ( row [ 4 ] , " TEST_EDI_LIGHT " ) ;
2019-03-18 15:42:22 +01:00
/* Show answers */
2019-05-30 12:57:31 +02:00
Tst_WriteAnswersGameResult ( GamCod , QstInd , QstCod ,
2017-09-15 11:39:02 +02:00
" TEST_EDI " , true ) ; // Show result
2017-09-06 23:17:52 +02:00
2017-09-06 15:03:43 +02:00
fprintf ( Gbl . F . Out , " </td> "
" </tr> " ) ;
/***** Destroy test question *****/
Tst_QstDestructor ( ) ;
}
/***** End table *****/
Tbl_EndTable ( ) ;
/***** Button to add a new question *****/
2017-09-07 18:38:18 +02:00
Gam_PutButtonToAddNewQuestions ( ) ;
2017-09-06 15:03:43 +02:00
/***** End box *****/
Box_EndBox ( ) ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-07-16 19:39:20 +02:00
/***************** Put icon to add a new questions to game *******************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutIconToAddNewQuestions ( void )
2017-07-09 20:31:40 +02:00
{
2017-07-16 19:39:20 +02:00
extern const char * Txt_Add_questions ;
2017-07-09 20:31:40 +02:00
/***** Put form to create a new question *****/
2019-01-10 15:26:33 +01:00
Ico_PutContextualIconToAdd ( ActAddOneGamQst , NULL , Gam_PutParams ,
Txt_Add_questions ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-07-16 19:39:20 +02:00
/***************** Put button to add new questions to game *******************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_PutButtonToAddNewQuestions ( void )
2017-07-09 20:31:40 +02:00
{
2017-07-16 19:39:20 +02:00
extern const char * Txt_Add_questions ;
2017-07-09 20:31:40 +02:00
2018-11-09 20:47:39 +01:00
Frm_StartForm ( ActAddOneGamQst ) ;
2017-09-07 18:38:18 +02:00
Gam_PutParams ( ) ;
2017-07-16 19:39:20 +02:00
Btn_PutConfirmButton ( Txt_Add_questions ) ;
2018-11-09 20:47:39 +01:00
Frm_EndForm ( ) ;
2017-07-09 20:31:40 +02:00
}
2017-07-16 20:50:01 +02:00
/*****************************************************************************/
/******************** Add selected test questions to game ********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_AddTstQuestionsToGame ( void )
2017-07-16 20:50:01 +02:00
{
2017-07-18 20:34:32 +02:00
extern const char * Txt_You_must_select_one_ore_more_questions ;
2017-09-01 00:52:19 +02:00
struct Game Game ;
const char * Ptr ;
char LongStr [ 1 + 10 + 1 ] ;
2017-09-07 12:00:01 +02:00
long QstCod ;
2019-05-30 12:57:31 +02:00
unsigned MaxQstInd ;
2017-09-01 00:52:19 +02:00
/***** Get game code *****/
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-09-01 00:52:19 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
2017-07-18 20:34:32 +02:00
/***** Get selected questions *****/
/* Allocate space for selected question codes */
2017-09-07 18:38:18 +02:00
Gam_AllocateListSelectedQuestions ( ) ;
2017-07-18 20:34:32 +02:00
/* Get question codes */
Par_GetParMultiToText ( " QstCods " , Gbl . Games . ListQuestions ,
2017-09-07 18:38:18 +02:00
Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS ) ;
2017-07-18 20:34:32 +02:00
/* Check number of questions */
2017-09-07 18:38:18 +02:00
if ( Gam_CountNumQuestionsInList ( ) = = 0 ) // If no questions selected...
2017-07-18 20:34:32 +02:00
{ // ...write warning alert
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_WARNING , Txt_You_must_select_one_ore_more_questions ) ;
2017-07-18 20:34:32 +02:00
// TODO: Show form again!!!
}
2017-09-01 00:52:19 +02:00
/***** Insert questions in database *****/
Ptr = Gbl . Games . ListQuestions ;
while ( * Ptr )
{
/* Get next code */
Par_GetNextStrUntilSeparParamMult ( & Ptr , LongStr , 1 + 10 ) ;
2017-09-07 12:00:01 +02:00
if ( sscanf ( LongStr , " %ld " , & QstCod ) ! = 1 )
2017-09-01 00:52:19 +02:00
Lay_ShowErrorAndExit ( " Wrong question code. " ) ;
2017-09-11 19:06:46 +02:00
/* Get current maximum index */
MaxQstInd = Gam_GetMaxQuestionIndexInGame ( Game . GamCod ) ; // -1 if no questions
2017-09-01 00:52:19 +02:00
/* Insert question in the table of questions */
2018-11-02 19:37:11 +01:00
DB_QueryINSERT ( " can not create question " ,
" INSERT INTO gam_questions "
" (GamCod,QstCod,QstInd) "
" VALUES "
" (%ld,%ld,%u) " ,
2019-05-30 12:57:31 +02:00
Game . GamCod , QstCod , MaxQstInd + 1 ) ;
2017-09-01 00:52:19 +02:00
}
/***** Free space for selected question codes *****/
2017-09-07 18:38:18 +02:00
Gam_FreeListsSelectedQuestions ( ) ;
2017-09-07 12:00:01 +02:00
2017-09-07 18:38:18 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-07-18 20:34:32 +02:00
}
/*****************************************************************************/
/****************** Allocate memory for list of questions ********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_AllocateListSelectedQuestions ( void )
2017-07-18 20:34:32 +02:00
{
if ( ! Gbl . Games . ListQuestions )
{
2017-09-07 18:38:18 +02:00
if ( ( Gbl . Games . ListQuestions = ( char * ) malloc ( Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS + 1 ) ) = = NULL )
2018-10-18 20:06:54 +02:00
Lay_NotEnoughMemoryExit ( ) ; ;
2017-07-18 20:34:32 +02:00
Gbl . Games . ListQuestions [ 0 ] = ' \0 ' ;
}
}
/*****************************************************************************/
/*********** Free memory used by list of selected question codes *************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static void Gam_FreeListsSelectedQuestions ( void )
2017-07-18 20:34:32 +02:00
{
if ( Gbl . Games . ListQuestions )
{
free ( ( void * ) Gbl . Games . ListQuestions ) ;
Gbl . Games . ListQuestions = NULL ;
}
}
/*****************************************************************************/
/**** Count the number of questions in the list of selected question codes ***/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
static unsigned Gam_CountNumQuestionsInList ( void )
2017-07-18 20:34:32 +02:00
{
const char * Ptr ;
unsigned NumQuestions = 0 ;
2017-09-01 00:52:19 +02:00
char LongStr [ 1 + 10 + 1 ] ;
2017-07-18 20:34:32 +02:00
long QstCod ;
/***** Go over the list Gbl.Test.ListAnsTypes counting the number of types of answer *****/
Ptr = Gbl . Games . ListQuestions ;
while ( * Ptr )
{
Par_GetNextStrUntilSeparParamMult ( & Ptr , LongStr , 1 + 10 ) ;
if ( sscanf ( LongStr , " %ld " , & QstCod ) ! = 1 )
Lay_ShowErrorAndExit ( " Wrong question code. " ) ;
NumQuestions + + ;
}
return NumQuestions ;
2017-07-16 20:50:01 +02:00
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-06 23:17:52 +02:00
/*** Get number of users who selected this answer and draw proportional bar **/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
void Gam_GetAndDrawBarNumUsrsWhoAnswered ( long GamCod , unsigned QstInd , unsigned AnsInd , unsigned NumUsrs )
2017-09-06 23:17:52 +02:00
{
unsigned NumUsrsThisAnswer ;
2017-07-09 20:31:40 +02:00
2017-09-06 23:17:52 +02:00
/***** Get number of users who selected this answer *****/
2019-05-30 12:57:31 +02:00
NumUsrsThisAnswer = Gam_GetNumUsrsWhoAnswered ( GamCod , QstInd , AnsInd ) ;
2017-07-09 20:31:40 +02:00
2017-09-06 23:17:52 +02:00
/***** Show stats of this answer *****/
2019-05-28 15:06:53 +02:00
Gam_DrawBarNumUsrs ( NumUsrsThisAnswer , NumUsrs ) ;
2017-07-09 20:31:40 +02:00
}
2017-09-06 23:17:52 +02:00
2019-07-19 13:11:59 +02:00
/*****************************************************************************/
/***** Get number of users who have answered current question in a match *****/
/*****************************************************************************/
static unsigned Gam_GetNumAnswerers ( struct Match * Match )
{
/***** Get number of users who have answered the current question in a match from database *****/
return
( unsigned ) DB_QueryCOUNT ( " can not get number of questions of a game " ,
" SELECT COUNT(*) FROM gam_answers "
" WHERE MchCod=%ld AND QstInd=%u " ,
Match - > MchCod , Match - > Status . QstInd ) ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-06 23:17:52 +02:00
/**** Get number of users who selected a given answer of a game question *****/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-05-30 12:57:31 +02:00
static unsigned Gam_GetNumUsrsWhoAnswered ( long GamCod , unsigned QstInd , unsigned AnsInd )
2017-07-09 20:31:40 +02:00
{
2017-09-01 14:36:25 +02:00
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2017-09-06 23:17:52 +02:00
unsigned NumUsrs = 0 ; // Default returned value
2017-09-01 14:36:25 +02:00
2017-09-06 23:17:52 +02:00
/***** Get answers of a question from database *****/
2018-10-31 10:19:01 +01:00
if ( DB_QuerySELECT ( & mysql_res , " can not get number of users who answered " ,
" SELECT NumUsrs FROM gam_answers "
2019-05-30 12:57:31 +02:00
" WHERE GamCod=%ld AND QstInd=%u AND AnsInd=%u " ,
GamCod , QstInd , AnsInd ) )
2017-09-01 14:36:25 +02:00
{
row = mysql_fetch_row ( mysql_res ) ;
2017-09-06 23:17:52 +02:00
if ( row [ 0 ] ) // There are users who selected this answer
if ( sscanf ( row [ 0 ] , " %u " , & NumUsrs ) ! = 1 )
Lay_ShowErrorAndExit ( " Error when getting number of users who answered. " ) ;
2017-07-09 20:31:40 +02:00
}
2017-09-06 23:17:52 +02:00
return NumUsrs ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/***************** Draw a bar with the percentage of answers *****************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
# define Gam_MAX_BAR_WIDTH 125
2017-07-09 20:31:40 +02:00
2017-09-07 18:38:18 +02:00
static void Gam_DrawBarNumUsrs ( unsigned NumUsrs , unsigned MaxUsrs )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_of_PART_OF_A_TOTAL ;
unsigned BarWidth = 0 ;
/***** String with the number of users *****/
if ( MaxUsrs )
2018-10-18 02:02:32 +02:00
snprintf ( Gbl . Title , sizeof ( Gbl . Title ) ,
" %u (%u%% %s %u) " ,
NumUsrs ,
( unsigned ) ( ( ( ( float ) NumUsrs * 100.0 ) / ( float ) MaxUsrs ) + 0.5 ) ,
Txt_of_PART_OF_A_TOTAL , MaxUsrs ) ;
2017-07-09 20:31:40 +02:00
else
2018-10-18 02:02:32 +02:00
snprintf ( Gbl . Title , sizeof ( Gbl . Title ) ,
" 0 (0%% %s %u) " ,
Txt_of_PART_OF_A_TOTAL , MaxUsrs ) ;
2017-07-09 20:31:40 +02:00
/***** Draw bar with a with proportional to the number of clicks *****/
if ( NumUsrs & & MaxUsrs )
2017-09-07 18:38:18 +02:00
BarWidth = ( unsigned ) ( ( ( ( float ) NumUsrs * ( float ) Gam_MAX_BAR_WIDTH ) /
2017-07-09 20:31:40 +02:00
( float ) MaxUsrs ) + 0.5 ) ;
if ( BarWidth < 2 )
BarWidth = 2 ;
fprintf ( Gbl . F . Out , " <img src= \" %s/c1x16.gif \" "
" alt= \" %s \" title= \" %s \" "
" class= \" LEFT_TOP \" "
" style= \" width:%upx; height:20px; \" /> "
" " ,
2019-03-20 01:36:36 +01:00
Cfg_URL_ICON_PUBLIC ,
2017-07-09 20:31:40 +02:00
Gbl . Title ,
Gbl . Title ,
BarWidth ) ;
/***** Write the number of users *****/
2019-05-20 19:47:17 +02:00
fprintf ( Gbl . F . Out , " %s " , Gbl . Title ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-08 01:18:20 +02:00
/**************** Put parameter to move/remove one question ******************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-08 01:18:20 +02:00
static void Gam_PutParamsOneQst ( void )
2017-07-09 20:31:40 +02:00
{
2019-05-20 08:52:07 +02:00
Gam_PutParamGameCod ( Gam_CurrentGamCod ) ;
2019-05-30 12:57:31 +02:00
Gam_PutParamQstInd ( Gam_CurrentQstInd ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/********************** Request the removal of a question ********************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RequestRemoveQst ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Do_you_really_want_to_remove_the_question_X ;
extern const char * Txt_Remove_question ;
2017-09-01 00:52:19 +02:00
struct Game Game ;
2017-09-07 12:00:01 +02:00
unsigned QstInd ;
2017-07-09 20:31:40 +02:00
2019-05-28 15:06:53 +02:00
/***** Get parameters *****/
2017-07-09 20:31:40 +02:00
/* Get game code */
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/* Get question index */
2019-05-30 12:57:31 +02:00
QstInd = Gam_GetParamQstInd ( ) ;
2017-07-09 20:31:40 +02:00
/***** Show question and button to remove question *****/
2019-05-20 08:52:07 +02:00
Gam_CurrentGamCod = Game . GamCod ;
2019-05-30 12:57:31 +02:00
Gam_CurrentQstInd = QstInd ;
2019-02-17 01:14:55 +01:00
Ale_ShowAlertAndButton ( ActRemGamQst , NULL , NULL , Gam_PutParamsOneQst ,
Btn_REMOVE_BUTTON , Txt_Remove_question ,
Ale_QUESTION , Txt_Do_you_really_want_to_remove_the_question_X ,
2019-05-30 12:57:31 +02:00
QstInd ) ;
2017-07-09 20:31:40 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
/****************************** Remove a question ****************************/
/*****************************************************************************/
2017-09-07 18:38:18 +02:00
void Gam_RemoveQst ( void )
2017-07-09 20:31:40 +02:00
{
extern const char * Txt_Question_removed ;
2017-09-01 00:52:19 +02:00
struct Game Game ;
2017-09-07 12:00:01 +02:00
unsigned QstInd ;
2017-07-09 20:31:40 +02:00
2019-05-28 15:06:53 +02:00
/***** Get parameters *****/
2017-07-09 20:31:40 +02:00
/* Get game code */
2017-09-07 18:38:18 +02:00
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-07-09 20:31:40 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/* Get question index */
2019-05-30 12:57:31 +02:00
QstInd = Gam_GetParamQstInd ( ) ;
2017-07-09 20:31:40 +02:00
/***** Remove the question from all the tables *****/
/* Remove answers from this test question */
2019-05-30 12:57:31 +02:00
Gam_RemAnswersOfAQuestion ( Game . GamCod , QstInd ) ;
2017-07-09 20:31:40 +02:00
/* Remove the question itself */
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ( " can not remove a question " ,
2019-05-30 12:57:31 +02:00
" DELETE FROM gam_questions "
" WHERE GamCod=%ld AND QstInd=%u " ,
Game . GamCod , QstInd ) ;
2017-07-09 20:31:40 +02:00
if ( ! mysql_affected_rows ( & Gbl . mysql ) )
Lay_ShowErrorAndExit ( " The question to be removed does not exist. " ) ;
/* Change index of questions greater than this */
2019-05-30 12:57:31 +02:00
DB_QueryUPDATE ( " can not update indexes of questions in table of answers " ,
" UPDATE gam_answers SET QstInd=QstInd-1 "
" WHERE GamCod=%ld AND QstInd>%u " ,
Game . GamCod , QstInd ) ;
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not update indexes of questions " ,
" UPDATE gam_questions SET QstInd=QstInd-1 "
" WHERE GamCod=%ld AND QstInd>%u " ,
Game . GamCod , QstInd ) ;
2017-07-09 20:31:40 +02:00
/***** Write message *****/
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_Question_removed ) ;
2017-07-09 20:31:40 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-07-09 20:31:40 +02:00
}
2017-09-11 19:06:46 +02:00
/*****************************************************************************/
/***************** Move up position of a question in a game ******************/
/*****************************************************************************/
void Gam_MoveUpQst ( void )
{
extern const char * Txt_The_question_has_been_moved_up ;
2019-05-30 12:57:31 +02:00
extern const char * Txt_Movement_not_allowed ;
2017-09-11 19:06:46 +02:00
struct Game Game ;
2019-05-30 12:57:31 +02:00
unsigned QstIndTop ;
unsigned QstIndBottom ;
2017-09-11 19:06:46 +02:00
2019-05-28 15:06:53 +02:00
/***** Get parameters *****/
2017-09-11 19:06:46 +02:00
/* Get game code */
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/* Get question index */
2019-05-30 12:57:31 +02:00
QstIndBottom = Gam_GetParamQstInd ( ) ;
2017-09-11 19:06:46 +02:00
/***** Move up question *****/
2019-05-30 12:57:31 +02:00
if ( QstIndBottom > 1 )
2017-09-11 19:06:46 +02:00
{
/* Indexes of questions to be exchanged */
QstIndTop = Gam_GetPrevQuestionIndexInGame ( Game . GamCod , QstIndBottom ) ;
2019-05-30 12:57:31 +02:00
if ( ! QstIndTop )
2017-09-14 02:32:36 +02:00
Lay_ShowErrorAndExit ( " Wrong index of question. " ) ;
2017-09-11 19:06:46 +02:00
/* Exchange questions */
2019-05-30 12:57:31 +02:00
Gam_ExchangeQuestions ( Game . GamCod , QstIndTop , QstIndBottom ) ;
2017-09-11 19:06:46 +02:00
/* Success alert */
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_The_question_has_been_moved_up ) ;
2017-09-11 19:06:46 +02:00
}
2019-05-30 12:57:31 +02:00
else
Ale_ShowAlert ( Ale_WARNING , Txt_Movement_not_allowed ) ;
2017-09-11 19:06:46 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-09-11 19:06:46 +02:00
}
/*****************************************************************************/
/**************** Move down position of a question in a game *****************/
/*****************************************************************************/
void Gam_MoveDownQst ( void )
{
extern const char * Txt_The_question_has_been_moved_down ;
2019-05-30 12:57:31 +02:00
extern const char * Txt_Movement_not_allowed ;
extern const char * Txt_This_game_has_no_questions ;
2017-09-11 19:06:46 +02:00
struct Game Game ;
2019-05-30 12:57:31 +02:00
unsigned QstIndTop ;
unsigned QstIndBottom ;
unsigned MaxQstInd ; // 0 if no questions
2017-09-11 19:06:46 +02:00
2019-05-28 15:06:53 +02:00
/***** Get parameters *****/
2017-09-11 19:06:46 +02:00
/* Get game code */
if ( ( Game . GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/* Get question index */
2019-05-30 12:57:31 +02:00
QstIndTop = Gam_GetParamQstInd ( ) ;
2017-09-11 19:06:46 +02:00
/* Get maximum question index */
MaxQstInd = Gam_GetMaxQuestionIndexInGame ( Game . GamCod ) ;
/***** Move down question *****/
2019-05-30 12:57:31 +02:00
if ( MaxQstInd )
{
2017-09-14 02:32:36 +02:00
if ( QstIndTop < MaxQstInd )
2017-09-11 19:06:46 +02:00
{
/* Indexes of questions to be exchanged */
QstIndBottom = Gam_GetNextQuestionIndexInGame ( Game . GamCod , QstIndTop ) ;
2019-05-30 12:57:31 +02:00
if ( ! QstIndBottom )
2017-09-14 02:32:36 +02:00
Lay_ShowErrorAndExit ( " Wrong index of question. " ) ;
2017-09-11 19:06:46 +02:00
/* Exchange questions */
2019-05-30 12:57:31 +02:00
Gam_ExchangeQuestions ( Game . GamCod , QstIndTop , QstIndBottom ) ;
2017-09-11 19:06:46 +02:00
/* Success alert */
2019-02-16 16:18:54 +01:00
Ale_ShowAlert ( Ale_SUCCESS , Txt_The_question_has_been_moved_down ) ;
2017-09-11 19:06:46 +02:00
}
2019-05-30 12:57:31 +02:00
else
Ale_ShowAlert ( Ale_WARNING , Txt_Movement_not_allowed ) ;
}
else
Ale_ShowAlert ( Ale_WARNING , Txt_This_game_has_no_questions ) ;
2017-09-11 19:06:46 +02:00
/***** Show current game *****/
2017-09-13 16:24:29 +02:00
Gam_ShowOneGame ( Game . GamCod ,
true , // Show only this game
true , // List game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2017-09-11 19:06:46 +02:00
}
/*****************************************************************************/
/********* Exchange the order of two consecutive questions in a game *********/
/*****************************************************************************/
static void Gam_ExchangeQuestions ( long GamCod ,
unsigned QstIndTop , unsigned QstIndBottom )
{
long QstCodTop ;
long QstCodBottom ;
/***** Lock table to make the inscription atomic *****/
2018-11-02 22:41:02 +01:00
DB_Query ( " can not lock tables to move game question " ,
" LOCK TABLES gam_questions WRITE " ) ;
2017-09-11 19:06:46 +02:00
Gbl . DB . LockedTables = true ;
/***** Get question code of the questions to be moved *****/
QstCodTop = Gam_GetQstCodFromQstInd ( GamCod , QstIndTop ) ;
QstCodBottom = Gam_GetQstCodFromQstInd ( GamCod , QstIndBottom ) ;
/***** Exchange indexes of questions *****/
/*
Example :
2019-05-30 12:57:31 +02:00
QstIndTop = 1 ; QstCodTop = 218
QstIndBottom = 2 ; QstCodBottom = 220
2017-09-11 19:06:46 +02:00
+ - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - +
| QstInd | QstCod | | QstInd | QstCod | | QstInd | QstCod |
+ - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - +
2019-05-30 12:57:31 +02:00
| 1 | 218 | - - - - - > | 2 | 218 | = | 1 | 220 |
| 2 | 220 | | 1 | 220 | | 2 | 218 |
| 3 | 232 | | 3 | 232 | | 3 | 232 |
2017-09-11 19:06:46 +02:00
+ - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - + + - - - - - - - - + - - - - - - - - +
*/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ( " can not exchange indexes of questions " ,
" UPDATE gam_questions SET QstInd=%u "
" WHERE GamCod=%ld AND QstCod=%ld " ,
QstIndBottom ,
GamCod , QstCodTop ) ;
DB_QueryUPDATE ( " can not exchange indexes of questions " ,
" UPDATE gam_questions SET QstInd=%u "
" WHERE GamCod=%ld AND QstCod=%ld " ,
QstIndTop ,
GamCod , QstCodBottom ) ;
2017-09-11 19:06:46 +02:00
/***** Unlock table *****/
Gbl . DB . LockedTables = false ; // Set to false before the following unlock...
// ...to not retry the unlock if error in unlocking
2018-11-02 22:41:02 +01:00
DB_Query ( " can not unlock tables after moving game questions " ,
" UNLOCK TABLES " ) ;
2017-09-11 19:06:46 +02:00
}
2019-05-28 15:06:53 +02:00
/*****************************************************************************/
/************************* List the matches of a game ************************/
/*****************************************************************************/
2019-07-04 19:45:15 +02:00
static void Gam_ListMatches ( struct Game * Game , bool PutFormNewMatch )
2019-05-28 15:06:53 +02:00
{
extern const char * Hlp_ASSESSMENT_Games_matches ;
extern const char * Txt_Matches ;
char * SubQuery ;
MYSQL_RES * mysql_res ;
unsigned NumMatches ;
/***** Get data of matches from database *****/
/* Fill subquery for game */
if ( Gbl . Crs . Grps . WhichGrps = = Grp_ONLY_MY_GROUPS )
{
2019-07-04 19:45:15 +02:00
if ( asprintf ( & SubQuery , " AND "
2019-05-28 15:06:53 +02:00
" (MchCod NOT IN "
" (SELECT MchCod FROM gam_grp) "
" OR "
" MchCod IN "
" (SELECT gam_grp.MchCod "
" FROM gam_grp,crs_grp_usr "
" WHERE crs_grp_usr.UsrCod=%ld "
" AND gam_grp.GrpCod=crs_grp_usr.GrpCod)) " ,
Gbl . Usrs . Me . UsrDat . UsrCod ) < 0 )
Lay_NotEnoughMemoryExit ( ) ;
}
else // Gbl.Crs.Grps.WhichGrps == Grp_ALL_GROUPS
2019-07-04 19:45:15 +02:00
if ( asprintf ( & SubQuery , " %s " , " " ) < 0 )
2019-05-28 15:06:53 +02:00
Lay_NotEnoughMemoryExit ( ) ;
/* Make query */
NumMatches = ( unsigned ) DB_QuerySELECT ( & mysql_res , " can not get matches " ,
2019-05-29 01:14:56 +02:00
" SELECT MchCod, " // row[ 0]
" GamCod, " // row[ 1]
" UsrCod, " // row[ 2]
" UNIX_TIMESTAMP(StartTime), " // row[ 3]
" UNIX_TIMESTAMP(EndTime), " // row[ 4]
" Title, " // row[ 5]
" QstInd, " // row[ 6]
" QstCod, " // row[ 7]
" UNIX_TIMESTAMP(QstStartTime), " // row[ 8]
2019-08-01 18:48:38 +02:00
" ShowingAnswers " // row[ 9]
2019-05-28 15:06:53 +02:00
" FROM gam_matches "
2019-07-04 19:45:15 +02:00
" WHERE GamCod=%ld%s "
2019-05-28 15:06:53 +02:00
" ORDER BY MchCod " ,
2019-07-04 19:45:15 +02:00
Game - > GamCod ,
2019-05-28 15:06:53 +02:00
SubQuery ) ;
/* Free allocated memory for subquery */
free ( ( void * ) SubQuery ) ;
/***** Start box *****/
Gam_CurrentGamCod = Game - > GamCod ;
Box_StartBox ( NULL , Txt_Matches , Gam_PutIconToPlayNewMatch ,
Hlp_ASSESSMENT_Games_matches , Box_NOT_CLOSABLE ) ;
if ( NumMatches )
/***** Show the table with the matches *****/
2019-07-04 19:45:15 +02:00
Gam_ListOneOrMoreMatches ( Game , NumMatches , mysql_res ) ;
2019-05-28 15:06:53 +02:00
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
/***** Put button to play a new match in this game *****/
2019-07-04 19:45:15 +02:00
switch ( Gbl . Usrs . Me . Role . Logged )
{
case Rol_NET :
case Rol_TCH :
case Rol_SYS_ADM :
if ( PutFormNewMatch )
Gam_PutFormNewMatch ( Game ) ; // Form to fill in data and start playing a new match
else
Gam_PutButtonNewMatch ( Game - > GamCod ) ; // Button to create a new match
break ;
default :
break ;
}
2019-05-28 15:06:53 +02:00
/***** End box *****/
Box_EndBox ( ) ;
}
/*****************************************************************************/
/********************** Get match data using its code ************************/
/*****************************************************************************/
void Gam_GetDataOfMatchByCod ( struct Match * Match )
{
MYSQL_RES * mysql_res ;
unsigned long NumRows ;
/***** Get data of match from database *****/
NumRows = ( unsigned ) DB_QuerySELECT ( & mysql_res , " can not get matches " ,
2019-05-29 01:14:56 +02:00
" SELECT MchCod, " // row[ 0]
" GamCod, " // row[ 1]
" UsrCod, " // row[ 2]
" UNIX_TIMESTAMP(StartTime), " // row[ 3]
" UNIX_TIMESTAMP(EndTime), " // row[ 4]
" Title, " // row[ 5]
" QstInd, " // row[ 6]
" QstCod, " // row[ 7]
" UNIX_TIMESTAMP(QstStartTime), " // row[ 8]
2019-08-01 18:48:38 +02:00
" ShowingAnswers " // row[ 9]
2019-05-28 15:06:53 +02:00
" FROM gam_matches "
" WHERE MchCod=%ld "
" AND GamCod IN " // Extra check
" (SELECT GamCod FROM games "
" WHERE CrsCod='%ld') " ,
Match - > MchCod ,
Gbl . Hierarchy . Crs . CrsCod ) ;
if ( NumRows ) // Match found...
/***** Get match data from row *****/
Gam_GetMatchDataFromRow ( mysql_res , Match ) ;
else
{
/* Initialize to empty match */
Match - > MchCod = - 1L ;
Match - > GamCod = - 1L ;
Match - > UsrCod = - 1L ;
Match - > TimeUTC [ Gam_START_TIME ] =
Match - > TimeUTC [ Gam_END_TIME ] = ( time_t ) 0 ;
Match - > Title [ 0 ] = ' \0 ' ;
2019-05-30 12:57:31 +02:00
Match - > Status . QstInd = 0 ;
2019-05-29 01:14:56 +02:00
Match - > Status . QstCod = - 1L ;
Match - > Status . QstStartTimeUTC = ( time_t ) 0 ;
2019-07-04 21:39:30 +02:00
Match - > Status . ShowingAnswers = false ;
2019-07-17 18:17:44 +02:00
Match - > Status . BeingPlayed = false ;
2019-05-28 15:06:53 +02:00
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
}
/*****************************************************************************/
/***************** Put icon to add a new questions to game *******************/
/*****************************************************************************/
static void Gam_PutIconToPlayNewMatch ( void )
{
extern const char * Txt_New_match ;
/***** Put form to create a new question *****/
2019-07-17 11:31:42 +02:00
Ico_PutContextualIconToAdd ( ActReqNewMchTch , Gam_NEW_MATCH_SECTION_ID , Gam_PutParams ,
2019-05-28 15:06:53 +02:00
Txt_New_match ) ;
}
/*****************************************************************************/
/*********************** List game matches for edition ***********************/
/*****************************************************************************/
2019-07-04 19:45:15 +02:00
static void Gam_ListOneOrMoreMatches ( struct Game * Game ,
2019-07-09 22:44:41 +02:00
unsigned NumMatches ,
MYSQL_RES * mysql_res )
2019-05-28 15:06:53 +02:00
{
extern const char * Txt_No_INDEX ;
extern const char * Txt_ROLES_SINGUL_Abc [ Rol_NUM_ROLES ] [ Usr_NUM_SEXS ] ;
extern const char * Txt_START_END_TIME [ Dat_NUM_START_END_TIME ] ;
extern const char * Txt_Match ;
2019-05-30 13:46:57 +02:00
extern const char * Txt_Status ;
2019-07-04 19:45:15 +02:00
extern const char * Txt_Play ;
2019-07-18 22:48:27 +02:00
extern const char * Txt_Resume ;
2019-05-28 15:06:53 +02:00
extern const char * Txt_Today ;
2019-05-31 10:25:20 +02:00
extern const char * Txt_View_game_results ;
2019-05-28 15:06:53 +02:00
unsigned NumMatch ;
unsigned UniqueId ;
struct Match Match ;
/***** Write the heading *****/
Tbl_StartTableWideMargin ( 2 ) ;
fprintf ( Gbl . F . Out , " <tr> "
" <th></th> "
2019-05-29 01:14:56 +02:00
" <th class= \" RIGHT_TOP \" > "
2019-05-28 15:06:53 +02:00
" %s "
" </th> "
2019-05-29 01:14:56 +02:00
" <th class= \" LEFT_TOP \" > "
2019-05-28 15:06:53 +02:00
" %s "
" </th> "
2019-05-29 01:14:56 +02:00
" <th class= \" LEFT_TOP \" > "
2019-05-28 15:06:53 +02:00
" %s "
" </th> "
2019-05-29 01:14:56 +02:00
" <th class= \" LEFT_TOP \" > "
2019-05-28 15:06:53 +02:00
" %s "
" </th> "
2019-05-29 01:14:56 +02:00
" <th class= \" LEFT_TOP \" > "
2019-05-28 15:06:53 +02:00
" %s "
" </th> "
2019-05-31 10:04:13 +02:00
" <th class= \" RIGHT_TOP \" > "
2019-05-30 13:46:57 +02:00
" %s "
" </th> "
2019-05-28 15:06:53 +02:00
" </tr> " ,
Txt_No_INDEX ,
Txt_ROLES_SINGUL_Abc [ Rol_TCH ] [ Usr_SEX_UNKNOWN ] ,
Txt_START_END_TIME [ Gam_ORDER_BY_START_DATE ] ,
Txt_START_END_TIME [ Gam_ORDER_BY_END_DATE ] ,
2019-05-30 13:46:57 +02:00
Txt_Match ,
Txt_Status ) ;
2019-05-28 15:06:53 +02:00
/***** Write rows *****/
for ( NumMatch = 0 , UniqueId = 1 ;
NumMatch < NumMatches ;
NumMatch + + , UniqueId + + )
{
Gbl . RowEvenOdd = NumMatch % 2 ;
/***** Get match data from row *****/
Gam_GetMatchDataFromRow ( mysql_res , & Match ) ;
/***** Icons *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" BT%u \" > " , Gbl . RowEvenOdd ) ;
/* Put icon to remove the match */
2019-07-16 22:33:28 +02:00
Frm_StartForm ( ActReqRemMchTch ) ;
2019-05-28 15:06:53 +02:00
Gam_PutParamMatchCod ( Match . MchCod ) ;
Ico_PutIconRemove ( ) ;
Frm_EndForm ( ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
/***** Number of match ******/
2019-05-30 13:46:57 +02:00
fprintf ( Gbl . F . Out , " <td class= \" BIG_INDEX RIGHT_TOP COLOR%u \" >%u</td> " ,
Gbl . RowEvenOdd , NumMatch + 1 ) ;
2019-05-28 15:06:53 +02:00
/***** Match player *****/
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP COLOR%u \" > " ,
Gbl . RowEvenOdd ) ;
Usr_WriteAuthor1Line ( Match . UsrCod , false ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
/***** Start date/time *****/
fprintf ( Gbl . F . Out , " <td id= \" mch_date_start_%u \" "
" class= \" %s LEFT_TOP COLOR%u \" > " ,
UniqueId ,
2019-08-01 18:48:38 +02:00
Match . Status . QstInd > = Gam_AFTER_LAST_QUESTION ? " DATE_RED " :
" DATE_GREEN " ,
2019-05-28 15:06:53 +02:00
Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " <script type= \" text/javascript \" > "
" writeLocalDateHMSFromUTC('mch_date_start_%u',%ld, "
" %u,'<br />','%s',true,true,0x7); "
" </script> "
" </td> " ,
UniqueId , Match . TimeUTC [ Gam_START_TIME ] ,
( unsigned ) Gbl . Prefs . DateFormat , Txt_Today ) ;
/***** End date/time *****/
fprintf ( Gbl . F . Out , " <td id= \" mch_date_end_%u \" "
" class= \" %s LEFT_TOP COLOR%u \" > " ,
UniqueId ,
2019-08-01 18:48:38 +02:00
Match . Status . QstInd > = Gam_AFTER_LAST_QUESTION ? " DATE_RED " :
" DATE_GREEN " ,
2019-05-28 15:06:53 +02:00
Gbl . RowEvenOdd ) ;
fprintf ( Gbl . F . Out , " \" > "
" <script type= \" text/javascript \" > "
" writeLocalDateHMSFromUTC('mch_date_end_%u',%ld, "
" %u,'<br />','%s',false,true,0x7); "
" </script> "
" </td> " ,
UniqueId , Match . TimeUTC [ Gam_END_TIME ] ,
( unsigned ) Gbl . Prefs . DateFormat , Txt_Today ) ;
/***** Title and groups *****/
2019-05-29 01:14:56 +02:00
fprintf ( Gbl . F . Out , " <td class= \" LEFT_TOP COLOR%u \" > " , Gbl . RowEvenOdd ) ;
2019-05-28 15:06:53 +02:00
/* Title */
2019-05-29 01:14:56 +02:00
fprintf ( Gbl . F . Out , " <span class= \" ASG_TITLE \" >%s</span> " , Match . Title ) ;
2019-05-28 15:06:53 +02:00
/* Groups whose students can answer this match */
if ( Gbl . Crs . Grps . NumGrps )
Gam_GetAndWriteNamesOfGrpsAssociatedToMatch ( & Match ) ;
fprintf ( Gbl . F . Out , " </td> " ) ;
2019-05-30 13:46:57 +02:00
/***** Match status ******/
2019-05-31 10:04:13 +02:00
fprintf ( Gbl . F . Out , " <td class= \" DAT RIGHT_TOP COLOR%u \" > " , Gbl . RowEvenOdd ) ;
2019-08-01 18:48:38 +02:00
if ( Match . Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished match
2019-05-31 10:04:13 +02:00
/* Icon to inform about finished match */
// Ico_PutIconOff ("flag-checkered.svg",Txt_Finished_match);
2019-07-16 22:33:28 +02:00
Lay_PutContextualLinkOnlyIcon ( ActShoMchTch , NULL ,
2019-05-31 10:04:13 +02:00
Gam_PutParamCurrentMchCod ,
" flag-checkered.svg " ,
2019-05-31 10:25:20 +02:00
Txt_View_game_results ) ;
2019-08-01 18:48:38 +02:00
else // Unfinished match
2019-05-30 13:46:57 +02:00
{
2019-05-31 10:04:13 +02:00
/* Current question index / total of questions */
fprintf ( Gbl . F . Out , " <div class= \" DAT \" >%u/%u</div> " ,
Match . Status . QstInd , Game - > NumQsts ) ;
2019-07-04 19:45:15 +02:00
switch ( Gbl . Usrs . Me . Role . Logged )
{
case Rol_STD :
/* Icon to play as student */
Gam_CurrentMchCod = Match . MchCod ;
Lay_PutContextualLinkOnlyIcon ( ActPlyMchStd , NULL ,
Gam_PutParamCurrentMchCod ,
" play.svg " ,
Txt_Play ) ;
break ;
case Rol_NET :
case Rol_TCH :
case Rol_DEG_ADM :
case Rol_CTR_ADM :
case Rol_INS_ADM :
case Rol_SYS_ADM :
/* Icon to resume */
Gam_CurrentMchCod = Match . MchCod ;
2019-07-16 22:33:28 +02:00
Lay_PutContextualLinkOnlyIcon ( ActResMchTch , NULL ,
2019-07-04 19:45:15 +02:00
Gam_PutParamCurrentMchCod ,
" play.svg " ,
2019-07-18 22:48:27 +02:00
Txt_Resume ) ;
2019-07-04 19:45:15 +02:00
break ;
default :
break ;
}
2019-05-30 13:46:57 +02:00
}
fprintf ( Gbl . F . Out , " </td> " ) ;
2019-05-28 15:06:53 +02:00
fprintf ( Gbl . F . Out , " </tr> " ) ;
}
/***** End table *****/
Tbl_EndTable ( ) ;
}
/*****************************************************************************/
/******************** Get game data from a database row **********************/
/*****************************************************************************/
static void Gam_GetMatchDataFromRow ( MYSQL_RES * mysql_res ,
struct Match * Match )
{
MYSQL_ROW row ;
/***** Get match data *****/
row = mysql_fetch_row ( mysql_res ) ;
/*
2019-05-29 01:14:56 +02:00
row [ 0 ] MchCod
row [ 1 ] GamCod
row [ 2 ] UsrCod
row [ 3 ] UNIX_TIMESTAMP ( StartTime )
row [ 4 ] UNIX_TIMESTAMP ( EndTime )
row [ 5 ] Title
2019-05-28 15:06:53 +02:00
*/
2019-05-29 01:14:56 +02:00
/***** Get match data *****/
/* Code of the match (row[0]) */
2019-05-28 15:06:53 +02:00
if ( ( Match - > MchCod = Str_ConvertStrCodToLongCod ( row [ 0 ] ) ) < = 0 )
Lay_ShowErrorAndExit ( " Wrong code of match. " ) ;
2019-05-29 01:14:56 +02:00
/* Code of the game (row[1]) */
2019-05-28 15:06:53 +02:00
if ( ( Match - > GamCod = Str_ConvertStrCodToLongCod ( row [ 1 ] ) ) < = 0 )
Lay_ShowErrorAndExit ( " Wrong code of game. " ) ;
2019-05-29 01:14:56 +02:00
/* Get match teacher (row[2]) */
Match - > UsrCod = Str_ConvertStrCodToLongCod ( row [ 2 ] ) ;
2019-05-28 15:06:53 +02:00
2019-05-29 01:14:56 +02:00
/* Get start date (row[3] holds the start UTC time) */
Match - > TimeUTC [ Gam_START_TIME ] = Dat_GetUNIXTimeFromStr ( row [ 3 ] ) ;
2019-05-28 15:06:53 +02:00
2019-05-29 01:14:56 +02:00
/* Get end date (row[4] holds the end UTC time) */
Match - > TimeUTC [ Gam_END_TIME ] = Dat_GetUNIXTimeFromStr ( row [ 4 ] ) ;
2019-05-28 15:06:53 +02:00
2019-05-29 01:14:56 +02:00
/* Get the title of the game (row[5]) */
if ( row [ 5 ] )
Str_Copy ( Match - > Title , row [ 5 ] ,
2019-05-28 15:06:53 +02:00
Gam_MAX_BYTES_TITLE ) ;
else
Match - > Title [ 0 ] = ' \0 ' ;
2019-05-29 01:14:56 +02:00
/***** Get current match status *****/
/*
row [ 6 ] QstInd
row [ 7 ] QstCod
row [ 8 ] UNIX_TIMESTAMP ( QstStartTime )
row [ 9 ] ShowingAnswers
*/
/* Current question index (row[6]) */
2019-05-30 12:57:31 +02:00
Match - > Status . QstInd = Gam_GetQstIndFromStr ( row [ 6 ] ) ;
2019-05-29 01:14:56 +02:00
/* Current question code (row[7]) */
Match - > Status . QstCod = Str_ConvertStrCodToLongCod ( row [ 7 ] ) ;
/* Get question start date (row[8] holds the start UTC time) */
Match - > Status . QstStartTimeUTC = Dat_GetUNIXTimeFromStr ( row [ 8 ] ) ;
/* Get whether to show question answers or not (row(9)) */
2019-05-30 20:52:23 +02:00
Match - > Status . ShowingAnswers = ( row [ 9 ] [ 0 ] = = ' Y ' ) ;
2019-05-29 01:14:56 +02:00
2019-07-17 18:17:44 +02:00
/***** Get whether the match is being played or not *****/
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished
2019-07-17 18:17:44 +02:00
Match - > Status . BeingPlayed = false ;
2019-08-01 18:48:38 +02:00
else // Unfinished
2019-07-17 18:17:44 +02:00
Match - > Status . BeingPlayed = Gam_GetIfMatchIsBeingPlayed ( Match - > MchCod ) ;
2019-05-28 15:06:53 +02:00
}
/*****************************************************************************/
/************** Request the removal of a match (game instance) ***************/
/*****************************************************************************/
2019-07-16 22:33:28 +02:00
void Gam_RequestRemoveMatchTch ( void )
2019-05-28 15:06:53 +02:00
{
extern const char * Txt_Do_you_really_want_to_remove_the_match_X ;
extern const char * Txt_Remove_match ;
struct Match Match ;
/***** Get parameters *****/
/* Get match code */
if ( ( Match . MchCod = Gam_GetParamMatchCod ( ) ) = = - 1L )
Lay_ShowErrorAndExit ( " Code of match is missing. " ) ;
/***** Get data of the match from database *****/
Gam_GetDataOfMatchByCod ( & Match ) ;
/***** Show question and button to remove question *****/
Gam_CurrentMchCod = Match . MchCod ;
2019-07-16 22:33:28 +02:00
Ale_ShowAlertAndButton ( ActRemMchTch , NULL , NULL , Gam_PutParamCurrentMchCod ,
2019-05-28 15:06:53 +02:00
Btn_REMOVE_BUTTON , Txt_Remove_match ,
Ale_QUESTION , Txt_Do_you_really_want_to_remove_the_match_X ,
Match . Title ) ;
/***** Show current game *****/
Gam_ShowOneGame ( Match . GamCod ,
true , // Show only this game
2019-07-04 21:39:30 +02:00
false , // Do not list game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2019-05-28 15:06:53 +02:00
}
/*****************************************************************************/
/********************** Remove a match (game instance) ***********************/
/*****************************************************************************/
2019-07-16 22:33:28 +02:00
void Gam_RemoveMatchTch ( void )
2019-05-28 15:06:53 +02:00
{
extern const char * Txt_Match_X_removed ;
struct Match Match ;
/***** Get parameters *****/
/* Get match code */
if ( ( Match . MchCod = Gam_GetParamMatchCod ( ) ) = = - 1L )
Lay_ShowErrorAndExit ( " Code of match is missing. " ) ;
/***** Get data of the match from database *****/
Gam_GetDataOfMatchByCod ( & Match ) ;
/***** Remove the match from all the tables *****/
2019-07-17 21:43:34 +02:00
/* Remove match players */
DB_QueryDELETE ( " can not remove match players " ,
" DELETE FROM gam_players "
" USING gam_players,gam_matches,games "
" WHERE gam_players.MchCod=%ld "
" AND gam_players.MchCod=gam_matches.MchCod "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
Match . MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
/* Remove match from list of matches being played */
DB_QueryDELETE ( " can not remove match from matches being played " ,
" DELETE FROM gam_mch_being_played "
" USING gam_mch_being_played,gam_matches,games "
" WHERE gam_mch_being_played.MchCod=%ld "
" AND gam_mch_being_played.MchCod=gam_matches.MchCod "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
Match . MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
2019-07-17 21:47:17 +02:00
/* Remove students' answers to match */
DB_QueryDELETE ( " can not remove students' answers associated to a match " ,
" DELETE FROM gam_answers "
" USING gam_answers,gam_matches,games "
" WHERE gam_answers.MchCod=%ld "
" AND gam_answers.MchCod=gam_matches.MchCod "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
Match . MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
2019-05-28 15:06:53 +02:00
/* Remove groups associated to the match */
2019-07-17 21:43:34 +02:00
DB_QueryDELETE ( " can not remove the groups associated to a match " ,
" DELETE FROM gam_grp "
" USING gam_grp,gam_matches,games "
2019-05-28 15:06:53 +02:00
" WHERE gam_grp.MchCod=%ld "
" AND gam_grp.MchCod=gam_matches.MchCod "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
2019-07-17 21:43:34 +02:00
Match . MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
2019-05-28 15:06:53 +02:00
/* Remove the match itself */
DB_QueryDELETE ( " can not remove a match " ,
2019-07-17 21:43:34 +02:00
" DELETE FROM gam_matches "
" USING gam_matches,games "
2019-05-28 15:06:53 +02:00
" WHERE gam_matches.MchCod=%ld "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
Match . MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
if ( ! mysql_affected_rows ( & Gbl . mysql ) )
Lay_ShowErrorAndExit ( " The match to be removed does not exist. " ) ;
/***** Write message *****/
Ale_ShowAlert ( Ale_SUCCESS , Txt_Match_X_removed ,
Match . Title ) ;
/***** Show current game *****/
Gam_ShowOneGame ( Match . GamCod ,
true , // Show only this game
2019-07-04 21:39:30 +02:00
false , // Do not list game questions
2019-07-04 19:45:15 +02:00
false ) ; // Do not put form to start new match
2019-05-28 15:06:53 +02:00
}
/*****************************************************************************/
/********************* Put button to create a new match **********************/
/*****************************************************************************/
static void Gam_PutButtonNewMatch ( long GamCod )
{
extern const char * Txt_New_match ;
2019-07-17 11:31:42 +02:00
Frm_StartFormAnchor ( ActReqNewMchTch , Gam_NEW_MATCH_SECTION_ID ) ;
2019-05-28 15:06:53 +02:00
Gam_PutParamGameCod ( GamCod ) ;
Btn_PutConfirmButton ( Txt_New_match ) ;
Frm_EndForm ( ) ;
}
2017-09-13 09:47:45 +02:00
/*****************************************************************************/
2019-05-20 10:35:57 +02:00
/******************* Start playing a game as a teacher ***********************/
2017-09-13 09:47:45 +02:00
/*****************************************************************************/
2019-07-16 22:33:28 +02:00
void Gam_RequestNewMatchTch ( void )
2017-09-13 09:47:45 +02:00
{
2019-05-20 10:35:57 +02:00
long GamCod ;
2017-09-13 09:47:45 +02:00
/***** Get parameters *****/
2019-07-04 17:17:15 +02:00
Gam_GetParamOrder ( ) ;
2017-09-13 09:47:45 +02:00
Grp_GetParamWhichGrps ( ) ;
2017-09-13 16:24:29 +02:00
Gbl . Games . CurrentPage = Pag_GetParamPagNum ( Pag_GAMES ) ;
2017-09-13 09:47:45 +02:00
/***** Get game code *****/
2019-05-20 10:35:57 +02:00
if ( ( GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2017-09-13 09:47:45 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
/***** Show game *****/
2019-05-20 10:35:57 +02:00
Gam_ShowOneGame ( GamCod ,
true , // Show only this game
2019-07-04 19:45:15 +02:00
false , // Do not list game questions
true ) ; // Put form to start new match
2019-05-20 10:35:57 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/****** Put a big button to play match (start a new match) as a teacher ******/
2019-05-20 10:35:57 +02:00
/*****************************************************************************/
2019-07-04 19:45:15 +02:00
static void Gam_PutFormNewMatch ( struct Game * Game )
2019-05-20 10:35:57 +02:00
{
2019-05-28 15:06:53 +02:00
extern const char * Hlp_ASSESSMENT_Games_new_match ;
extern const char * The_ClassFormInBox [ The_NUM_THEMES ] ;
extern const char * Txt_New_match ;
extern const char * Txt_Title ;
extern const char * Txt_Play ;
/***** Start section for a new match *****/
Lay_StartSection ( Gam_NEW_MATCH_SECTION_ID ) ;
2019-05-20 10:35:57 +02:00
/***** Start form *****/
2019-07-16 22:33:28 +02:00
Frm_StartForm ( ActNewMchTch ) ;
2019-05-28 15:06:53 +02:00
Gam_PutParamGameCod ( Game - > GamCod ) ;
2019-05-20 10:35:57 +02:00
Gam_PutParamQstInd ( 0 ) ; // Start by first question in game
2019-05-28 15:06:53 +02:00
/***** Start box and table *****/
Box_StartBoxTable ( NULL , Txt_New_match , NULL ,
Hlp_ASSESSMENT_Games_new_match , Box_NOT_CLOSABLE , 2 ) ;
/***** Match title *****/
fprintf ( Gbl . F . Out , " <tr> "
" <td class= \" RIGHT_MIDDLE \" > "
" <label for= \" Title \" class= \" %s \" >%s:</label> "
" </td> "
" <td class= \" LEFT_MIDDLE \" > "
" <input type= \" text \" id= \" Title \" name= \" Title \" "
" size= \" 45 \" maxlength= \" %u \" value= \" %s \" "
" required= \" required \" /> "
" </td> "
" </tr> " ,
The_ClassFormInBox [ Gbl . Prefs . Theme ] ,
Txt_Title ,
Gam_MAX_CHARS_TITLE , Game - > Title ) ;
/***** Groups *****/
2019-07-17 11:31:42 +02:00
Gam_ShowLstGrpsToCreateMatch ( ) ;
2019-05-28 15:06:53 +02:00
/***** End table *****/
Tbl_EndTable ( ) ;
2019-05-20 10:35:57 +02:00
/***** Put icon with link *****/
2019-05-28 15:06:53 +02:00
Frm_LinkFormSubmit ( Txt_Play , NULL , NULL ) ;
2019-05-20 10:35:57 +02:00
fprintf ( Gbl . F . Out , " <img src= \" %s/play.svg \" "
" alt= \" %s \" title= \" %s \" "
" class= \" CONTEXT_OPT ICO_HIGHLIGHT ICO64x64 \" /> " ,
2019-05-28 15:06:53 +02:00
Cfg_URL_ICON_PUBLIC , Txt_Play , Txt_Play ) ;
2019-05-20 10:35:57 +02:00
fprintf ( Gbl . F . Out , " </a> " ) ;
2019-05-28 15:06:53 +02:00
/***** End box *****/
Box_EndBox ( ) ;
2019-05-20 10:35:57 +02:00
/***** End form *****/
Frm_EndForm ( ) ;
2019-05-28 15:06:53 +02:00
/***** End section for a new match *****/
Lay_EndSection ( ) ;
2017-09-13 16:24:29 +02:00
}
2017-09-13 21:22:52 +02:00
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
/********************* Create a new match (by a teacher) *********************/
2017-09-13 21:22:52 +02:00
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
void Gam_CreateNewMatchTch ( void )
2017-09-14 02:32:36 +02:00
{
2019-07-16 22:33:28 +02:00
long GamCod ;
char Title [ Gam_MAX_BYTES_TITLE + 1 ] ;
2019-05-20 19:47:17 +02:00
2019-05-28 15:06:53 +02:00
/***** Get form parameters *****/
2019-07-04 21:39:30 +02:00
/* Get match code */
2019-07-16 22:33:28 +02:00
if ( ( GamCod = Gam_GetParamGameCod ( ) ) = = - 1L )
2019-05-20 19:47:17 +02:00
Lay_ShowErrorAndExit ( " Code of game is missing. " ) ;
2019-05-28 15:06:53 +02:00
/* Get match title */
2019-07-16 22:33:28 +02:00
Par_GetParToText ( " Title " , Title , Gam_MAX_BYTES_TITLE ) ;
2019-05-28 15:06:53 +02:00
/* Get groups for this games */
Grp_GetParCodsSeveralGrps ( ) ;
/***** Create a new match *****/
2019-07-16 22:33:28 +02:00
Gbl . Games . MchCodBeingPlayed = Gam_CreateMatch ( GamCod , Title ) ;
2019-05-28 15:06:53 +02:00
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ( ) ;
2019-07-16 22:33:28 +02:00
}
2019-07-17 11:31:42 +02:00
/*****************************************************************************/
/******* Show button to actually start / resume a match (by a teacher) *******/
/*****************************************************************************/
void Gam_RequestStartResumeMatchTch ( void )
2019-07-16 22:33:28 +02:00
{
struct Match Match ;
2019-07-18 22:10:54 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-07-16 22:33:28 +02:00
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
2019-05-28 15:06:53 +02:00
2019-07-30 15:21:29 +02:00
/***** Update match status in database *****/
Gam_UpdateMatchStatusInDB ( & Match ) ;
2019-07-04 21:39:30 +02:00
/***** Show current match status *****/
2019-07-18 15:16:36 +02:00
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
Gam_ShowMatchStatusForTch ( & Match ) ;
2019-07-16 22:33:28 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2017-09-14 02:32:36 +02:00
}
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/********************** Create a new match in a game *************************/
2017-09-14 02:32:36 +02:00
/*****************************************************************************/
2019-07-16 22:33:28 +02:00
static long Gam_CreateMatch ( long GamCod , char Title [ Gam_MAX_BYTES_TITLE + 1 ] )
2019-05-20 19:47:17 +02:00
{
2019-07-16 22:33:28 +02:00
long MchCod ;
2019-05-28 15:06:53 +02:00
2019-05-30 20:52:23 +02:00
/***** Insert this new match into database *****/
2019-07-16 22:33:28 +02:00
MchCod = DB_QueryINSERTandReturnCode ( " can not create match " ,
" INSERT gam_matches "
" (GamCod,UsrCod,StartTime,EndTime,Title, "
2019-08-01 18:48:38 +02:00
" QstInd,QstCod,QstStartTime,ShowingAnswers) "
2019-07-16 22:33:28 +02:00
" VALUES "
" (%ld, " // GamCod
" %ld, " // UsrCod
" NOW(), " // StartTime
" NOW(), " // EndTime
" '%s', " // Title
" 0, " // QstInd: Match has not started, so not the first question yet
" -1, " // QstCod: Non-existent question
" NOW(), " // QstStartTime
2019-08-01 18:48:38 +02:00
" 'N') " , // ShowingAnswers: Don't show answers initially
2019-07-16 22:33:28 +02:00
GamCod ,
Gbl . Usrs . Me . UsrDat . UsrCod , // Game creator
Title ) ;
2019-05-28 15:06:53 +02:00
/***** Create groups associated to the match *****/
if ( Gbl . Crs . Grps . LstGrpsSel . NumGrps )
2019-07-16 22:33:28 +02:00
Gam_CreateGrps ( MchCod ) ;
return MchCod ;
2019-05-28 15:06:53 +02:00
}
2019-05-20 19:47:17 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
/***************** Insert/update a game match being played *******************/
2019-05-20 19:47:17 +02:00
/*****************************************************************************/
2019-07-18 22:10:54 +02:00
static void Gam_UpdateMatchStatusInDB ( struct Match * Match )
2019-05-20 19:47:17 +02:00
{
2019-05-30 20:52:23 +02:00
/***** Update match status in database *****/
DB_QueryUPDATE ( " can not update match being played " ,
" UPDATE gam_matches,games "
" SET gam_matches.EndTime=NOW(), "
" gam_matches.QstInd=%u, "
" gam_matches.QstCod=%ld, "
2019-05-31 00:40:23 +02:00
" gam_matches.QstStartTime=NOW(), "
2019-08-01 18:48:38 +02:00
" gam_matches.ShowingAnswers='%c' "
2019-05-30 20:52:23 +02:00
" WHERE gam_matches.MchCod=%ld "
" AND gam_matches.GamCod=games.GamCod "
" AND games.CrsCod=%ld " , // Extra check
Match - > Status . QstInd , Match - > Status . QstCod ,
Match - > Status . ShowingAnswers ? ' Y ' :
' N ' ,
Match - > MchCod , Gbl . Hierarchy . Crs . CrsCod ) ;
2019-07-17 18:17:44 +02:00
2019-07-30 15:21:29 +02:00
if ( Match - > Status . BeingPlayed )
/* Update match as being played */
Gam_UpdateMatchAsBeingPlayed ( Match - > MchCod ) ;
else
2019-07-17 18:17:44 +02:00
/* Update match as not being played */
Gam_SetMatchAsNotBeingPlayed ( Match - > MchCod ) ;
2019-05-20 19:47:17 +02:00
}
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
/** Show current match status (current question, answers...) (by a teacher) **/
/*****************************************************************************/
2019-07-17 18:37:06 +02:00
void Gam_ResumeMatchTch ( void )
2019-07-17 11:31:42 +02:00
{
struct Match Match ;
2019-07-18 22:10:54 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-07-17 11:31:42 +02:00
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
/***** If not yet finished, update status *****/
2019-08-01 18:48:38 +02:00
if ( Match . Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-07-17 11:31:42 +02:00
{
if ( Match . Status . QstInd = = 0 ) // Match has been created, but it has not started
2019-07-17 22:45:20 +02:00
Gam_SetMatchStatusToNextQuestion ( & Match ) ;
Match . Status . ShowingAnswers = false ; // Don't show answers in any case
2019-07-18 15:16:36 +02:00
Match . Status . BeingPlayed = true ; // Resume match
2019-07-30 15:21:29 +02:00
}
2019-07-17 18:37:06 +02:00
2019-07-30 15:21:29 +02:00
/***** Update match status in database *****/
Gam_UpdateMatchStatusInDB ( & Match ) ;
/***** Show current match status *****/
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
Gam_ShowMatchStatusForTch ( & Match ) ;
fprintf ( Gbl . F . Out , " </div> " ) ;
}
/*****************************************************************************/
/* Show previous match status (previous question, answers...) (by a teacher) */
/*****************************************************************************/
void Gam_PrevStatusMatchTch ( void )
{
struct Match Match ;
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
2019-08-01 00:03:56 +02:00
/***** Update status *****/
2019-08-01 18:48:38 +02:00
if ( Match . Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished
2019-08-01 00:03:56 +02:00
Gam_SetMatchStatusToPrevQuestion ( & Match ) ;
2019-08-01 18:48:38 +02:00
else // Unfinished
2019-07-30 15:21:29 +02:00
{
2019-08-01 18:48:38 +02:00
if ( Match . Status . ShowingAnswers ) // Showing answers currently
Match . Status . ShowingAnswers = false ; // Do not show answers
2019-07-30 15:21:29 +02:00
else
Gam_SetMatchStatusToPrevQuestion ( & Match ) ;
2019-07-17 11:31:42 +02:00
}
2019-07-30 15:21:29 +02:00
/***** Update match status in database *****/
Gam_UpdateMatchStatusInDB ( & Match ) ;
2019-07-17 11:31:42 +02:00
/***** Show current match status *****/
2019-07-18 15:16:36 +02:00
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
2019-07-17 11:31:42 +02:00
Gam_ShowMatchStatusForTch ( & Match ) ;
fprintf ( Gbl . F . Out , " </div> " ) ;
}
/*****************************************************************************/
/***** Show next match status (next question, answers...) (by a teacher) *****/
2019-05-20 19:47:17 +02:00
/*****************************************************************************/
2019-07-16 22:33:28 +02:00
void Gam_NextStatusMatchTch ( void )
2017-09-14 02:32:36 +02:00
{
2019-05-30 20:52:23 +02:00
struct Match Match ;
2019-05-20 19:47:17 +02:00
2019-07-18 22:10:54 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-05-30 20:52:23 +02:00
/***** Get data of the match from database *****/
2019-07-16 22:33:28 +02:00
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
2019-05-30 20:52:23 +02:00
Gam_GetDataOfMatchByCod ( & Match ) ;
2017-09-14 02:32:36 +02:00
2019-05-30 20:52:23 +02:00
/***** If not yet finished, update status *****/
2019-08-01 18:48:38 +02:00
if ( Match . Status . QstInd = = 0 ) // Not started
Gam_SetMatchStatusToNextQuestion ( & Match ) ;
else if ( Match . Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-05-30 20:52:23 +02:00
{
2019-08-01 18:48:38 +02:00
if ( Match . Status . ShowingAnswers ) // Showing answers currently
2019-07-17 22:45:20 +02:00
Gam_SetMatchStatusToNextQuestion ( & Match ) ;
2019-08-01 18:48:38 +02:00
else
Match . Status . ShowingAnswers = true ; // Show answers
2019-05-30 20:52:23 +02:00
}
2017-09-13 21:22:52 +02:00
2019-07-30 15:21:29 +02:00
/***** Update match status in database *****/
Gam_UpdateMatchStatusInDB ( & Match ) ;
2019-07-04 21:39:30 +02:00
/***** Show current match status *****/
2019-07-18 15:16:36 +02:00
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
2019-07-04 21:39:30 +02:00
Gam_ShowMatchStatusForTch ( & Match ) ;
2019-07-16 22:33:28 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-04 21:39:30 +02:00
}
2019-07-30 15:21:29 +02:00
/*****************************************************************************/
/******************* Set match status to previous question *******************/
/*****************************************************************************/
static void Gam_SetMatchStatusToPrevQuestion ( struct Match * Match )
{
/***** Get index of the previous question *****/
Match - > Status . QstInd = Gam_GetPrevQuestionIndexInGame ( Match - > GamCod ,
Match - > Status . QstInd ) ;
2019-08-01 00:03:56 +02:00
if ( Match - > Status . QstInd = = 0 ) // Start of questions has been reached
2019-07-30 15:21:29 +02:00
{
Match - > Status . QstCod = - 1L ; // No previous questions
2019-07-31 22:44:15 +02:00
Match - > Status . BeingPlayed = false ; // Match is not being played
Match - > Status . ShowingAnswers = false ; // Do not show answers
2019-07-30 15:21:29 +02:00
}
2019-08-01 00:03:56 +02:00
else
{
Match - > Status . QstCod = Gam_GetQstCodFromQstInd ( Match - > GamCod ,
Match - > Status . QstInd ) ;
Match - > Status . BeingPlayed = true ; // Match is being played
Match - > Status . ShowingAnswers = true ; // Show answers
}
2019-07-30 15:21:29 +02:00
}
2019-07-17 22:45:20 +02:00
/*****************************************************************************/
/********************* Set match status to next question *********************/
/*****************************************************************************/
static void Gam_SetMatchStatusToNextQuestion ( struct Match * Match )
{
/***** Get index of the next question *****/
Match - > Status . QstInd = Gam_GetNextQuestionIndexInGame ( Match - > GamCod ,
Match - > Status . QstInd ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-07-17 22:45:20 +02:00
{
2019-08-01 00:03:56 +02:00
Match - > Status . QstCod = Gam_GetQstCodFromQstInd ( Match - > GamCod ,
Match - > Status . QstInd ) ;
2019-08-01 18:48:38 +02:00
Match - > Status . BeingPlayed = true ;
2019-07-17 22:45:20 +02:00
}
2019-08-01 18:48:38 +02:00
else // Finished
{
Match - > Status . QstCod = - 1L ; // No more questions
Match - > Status . BeingPlayed = false ;
}
Match - > Status . ShowingAnswers = false ; // Don't show answers
2019-07-17 22:45:20 +02:00
}
2019-07-04 21:39:30 +02:00
/*****************************************************************************/
/******* Show current match status (number, question, answers, button) *******/
/*****************************************************************************/
static void Gam_ShowMatchStatusForTch ( struct Match * Match )
{
2019-07-25 10:45:36 +02:00
/***** Get current number of players *****/
Gam_GetNumPlayers ( Match ) ;
2019-07-19 13:11:59 +02:00
/***** Left column *****/
2019-07-25 10:45:36 +02:00
Gam_ShowLeftColumnTch ( Match ) ;
2019-07-19 13:11:59 +02:00
/***** Right column *****/
/* Start right container */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_RIGHT \" > " ) ;
2019-07-18 15:16:36 +02:00
/***** Top row *****/
2019-07-31 15:31:02 +02:00
Gam_ShowMatchTitleAndCloseButton ( Match ) ;
2019-07-18 15:16:36 +02:00
/***** Bottom row *****/
2019-08-01 18:48:38 +02:00
if ( Match - > Status . BeingPlayed )
2019-07-17 18:17:44 +02:00
/* Show current question and possible answers */
2019-07-18 15:16:36 +02:00
Gam_ShowQuestionAndAnswersTch ( Match ) ;
2019-07-17 18:17:44 +02:00
2019-07-19 13:11:59 +02:00
/* End right container */
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-05-20 19:47:17 +02:00
}
2019-05-31 10:25:20 +02:00
/*****************************************************************************/
2019-07-18 22:48:27 +02:00
/************ Show current question being played for a student ***************/
2019-05-31 10:25:20 +02:00
/*****************************************************************************/
2019-07-18 22:48:27 +02:00
static void Gam_ShowMatchStatusForStd ( struct Match * Match )
2019-05-31 10:25:20 +02:00
{
2019-07-18 22:48:27 +02:00
extern const char * Txt_Please_wait_ ;
bool IBelongToGroups ;
/***** Do I belong to valid groups to play this match? *****/
IBelongToGroups = Gbl . Usrs . Me . IBelongToCurrentCrs & &
Gam_CheckIfIPlayThisMatchBasedOnGrps ( Match - > MchCod ) ;
if ( ! IBelongToGroups )
Lay_ShowErrorAndExit ( " You can not play this match! " ) ;
2019-07-25 10:45:36 +02:00
/***** Get current number of players *****/
Gam_GetNumPlayers ( Match ) ;
/***** Left column *****/
Gam_ShowLeftColumnStd ( Match ) ;
/***** Right column *****/
/* Start right container */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_RIGHT \" > " ) ;
2019-07-18 22:48:27 +02:00
2019-07-25 10:45:36 +02:00
/***** Top row *****/
2019-07-31 15:31:02 +02:00
Gam_ShowMatchTitleAndCloseButton ( Match ) ;
2019-07-18 22:48:27 +02:00
2019-07-25 10:45:36 +02:00
/***** Bottom row *****/
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-07-18 22:48:27 +02:00
{
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BOTTOM \" > " ) ;
2019-07-18 22:48:27 +02:00
/***** Update players ******/
Gam_RegisterMeAsPlayerInMatch ( Match - > MchCod ) ;
2019-05-31 10:25:20 +02:00
2019-07-18 22:48:27 +02:00
if ( Match - > Status . BeingPlayed )
/* Show current question and possible answers */
Gam_ShowQuestionAndAnswersStd ( Match ) ;
else // Not being played
fprintf ( Gbl . F . Out , " <div class= \" MATCH_STD_WAIT_CONTAINER \" > "
" <img src= \" %s/wait.gif \" "
" alt= \" %s \" title= \" %s \" "
" class= \" MATCH_STD_WAIT_IMAGE \" /> "
" </div> " ,
Cfg_URL_ICON_PUBLIC ,
Txt_Please_wait_ ,
Txt_Please_wait_ ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-18 22:48:27 +02:00
}
2019-05-31 10:25:20 +02:00
2019-07-25 10:45:36 +02:00
/* End right container */
2019-07-18 22:48:27 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-05-31 10:25:20 +02:00
}
2019-05-20 19:47:17 +02:00
/*****************************************************************************/
2019-07-18 15:16:36 +02:00
/******** Show left botton column when playing a match (as a teacher) ********/
2019-05-20 19:47:17 +02:00
/*****************************************************************************/
2019-07-25 10:45:36 +02:00
static void Gam_ShowLeftColumnTch ( struct Match * Match )
2019-05-20 19:47:17 +02:00
{
2019-08-01 18:48:38 +02:00
extern const char * Txt_MATCH_Start ;
extern const char * Txt_MATCH_End ;
2019-07-30 15:21:29 +02:00
extern const char * Txt_Stem ;
extern const char * Txt_Previous_QUESTION ;
extern const char * Txt_Start ;
2019-07-18 22:10:54 +02:00
extern const char * Txt_Next_QUESTION ;
2019-07-04 21:39:30 +02:00
extern const char * Txt_Finish ;
2019-07-18 22:10:54 +02:00
extern const char * Txt_Answers ;
2019-07-18 22:48:27 +02:00
extern const char * Txt_Start ;
extern const char * Txt_Resume ;
2019-07-30 15:21:29 +02:00
unsigned PrvQstInd ; // Previous question index
unsigned NxtQstInd ; // Next question index
2019-07-18 22:10:54 +02:00
unsigned NumQsts ;
2019-07-19 13:11:59 +02:00
unsigned NumAnswerers ;
2019-07-18 15:16:36 +02:00
2019-07-19 13:11:59 +02:00
/***** Start left container *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_LEFT \" > " ) ;
2019-07-18 15:16:36 +02:00
2019-07-25 10:45:36 +02:00
/***** Top *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_TOP \" ></div> " ) ;
2019-07-18 22:10:54 +02:00
/***** Write number of question *****/
NumQsts = Gam_GetNumQstsGame ( Match - > GamCod ) ;
2019-07-19 13:11:59 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_NUM_QST \" > " ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd = = 0 ) // Not started
fprintf ( Gbl . F . Out , " %s " , Txt_MATCH_Start ) ;
else if ( Match - > Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished
fprintf ( Gbl . F . Out , " %s " , Txt_MATCH_End ) ;
2019-07-18 22:10:54 +02:00
else
fprintf ( Gbl . F . Out , " %u/%u " , Match - > Status . QstInd , NumQsts ) ;
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-18 15:16:36 +02:00
2019-07-30 15:21:29 +02:00
/***** Buttons *****/
/* Start buttons container */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTONS_CONTAINER \" > " ) ;
/* Left button */
2019-07-31 22:44:15 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTON_LEFT_CONTAINER \" > " ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-07-30 15:21:29 +02:00
{
if ( Match - > Status . BeingPlayed )
{
if ( Match - > Status . ShowingAnswers )
2019-07-31 22:44:15 +02:00
/* Put button to hide answers */
2019-07-30 15:21:29 +02:00
Gam_PutBigButton ( ActCurMchTch , Match - > MchCod ,
2019-07-31 22:44:15 +02:00
" step-backward.svg " , Txt_Stem ) ;
2019-07-30 15:21:29 +02:00
else
{
/* Get index of the previous question */
PrvQstInd = Gam_GetPrevQuestionIndexInGame ( Match - > GamCod ,
Match - > Status . QstInd ) ;
2019-08-01 18:48:38 +02:00
if ( PrvQstInd = = 0 ) // There is not a previous question
/* Put button to resume match before first question */
Gam_PutBigButton ( ActPrvMchTch , Match - > MchCod ,
" step-backward.svg " , Txt_MATCH_Start ) ;
else // There is a previous question
2019-07-30 15:21:29 +02:00
/* Put button to show previous question */
Gam_PutBigButton ( ActPrvMchTch , Match - > MchCod ,
" step-backward.svg " , Txt_Previous_QUESTION ) ;
}
}
else // Not being played
/* Put button to close browser tab */
Gam_PutBigButtonClose ( ) ;
}
2019-08-01 18:48:38 +02:00
else // Finished
/* Put button to show last question */
Gam_PutBigButton ( ActPrvMchTch , Match - > MchCod ,
" step-backward.svg " , Txt_Previous_QUESTION ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
/* Right button */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTON_RIGHT_CONTAINER \" > " ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished
2019-07-18 22:10:54 +02:00
/* Put button to close browser tab */
Gam_PutBigButtonClose ( ) ;
else if ( Match - > Status . BeingPlayed )
{
2019-07-18 15:16:36 +02:00
if ( Match - > Status . ShowingAnswers )
{
/* Get index of the next question */
NxtQstInd = Gam_GetNextQuestionIndexInGame ( Match - > GamCod ,
Match - > Status . QstInd ) ;
2019-08-01 18:48:38 +02:00
if ( NxtQstInd > = Gam_AFTER_LAST_QUESTION ) // No more questions
2019-07-18 15:16:36 +02:00
/* Put button to finish */
Gam_PutBigButton ( ActNxtMchTch , Match - > MchCod ,
2019-08-01 18:48:38 +02:00
" step-forward.svg " , Txt_Finish ) ;
else // There are more questions
2019-08-01 00:03:56 +02:00
/* Put button to show next question */
Gam_PutBigButton ( ActNxtMchTch , Match - > MchCod ,
" step-forward.svg " , Txt_Next_QUESTION ) ;
2019-07-18 15:16:36 +02:00
}
else
/* Put button to show answers */
Gam_PutBigButton ( ActNxtMchTch , Match - > MchCod ,
2019-07-31 22:44:15 +02:00
" step-forward.svg " , Txt_Answers ) ;
2019-07-18 15:16:36 +02:00
}
else
/* Put button to start / resume match */
Gam_PutBigButton ( ActCurMchTch ,
Match - > MchCod ,
" play.svg " ,
2019-07-18 22:48:27 +02:00
Match - > Status . QstInd = = 0 ? Txt_Start :
Txt_Resume ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
/* End buttons container */
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-18 22:48:27 +02:00
2019-07-31 15:31:02 +02:00
/***** Number of players *****/
Gam_ShowNumPlayers ( Match ) ;
/***** Number of users who have answered *****/
2019-08-01 18:48:38 +02:00
if ( Match - > Status . BeingPlayed & &
Match - > Status . ShowingAnswers )
2019-07-19 13:11:59 +02:00
{
NumAnswerers = Gam_GetNumAnswerers ( Match ) ;
fprintf ( Gbl . F . Out , " <div class= \" MATCH_NUM_ANSWERERS \" > "
" responden<br /> " // TODO: Need translation!!!
" <strong>%u/%u</strong> "
" </div> " ,
2019-07-25 10:45:36 +02:00
NumAnswerers , Match - > Status . NumPlayers ) ;
2019-07-19 13:11:59 +02:00
}
/***** End left container *****/
2019-07-18 22:48:27 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
}
/*****************************************************************************/
/******** Show left botton column when playing a match (as a student) ********/
/*****************************************************************************/
2019-07-25 10:45:36 +02:00
static void Gam_ShowLeftColumnStd ( struct Match * Match )
2019-07-18 22:48:27 +02:00
{
2019-08-01 18:48:38 +02:00
extern const char * Txt_MATCH_Start ;
extern const char * Txt_MATCH_End ;
2019-07-18 22:48:27 +02:00
unsigned NumQsts ;
2019-07-25 10:45:36 +02:00
/***** Start left container *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_LEFT \" > " ) ;
/***** Top *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_TOP \" ></div> " ) ;
2019-07-18 22:48:27 +02:00
/***** Write number of question *****/
NumQsts = Gam_GetNumQstsGame ( Match - > GamCod ) ;
2019-07-19 13:11:59 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_NUM_QST \" > " ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd < Gam_AFTER_LAST_QUESTION ) // Unfinished
2019-07-18 22:48:27 +02:00
fprintf ( Gbl . F . Out , " %u/%u " , Match - > Status . QstInd , NumQsts ) ;
2019-08-01 18:48:38 +02:00
else // Finished
fprintf ( Gbl . F . Out , " %s " , Txt_MATCH_End ) ;
2019-07-18 22:48:27 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-30 15:21:29 +02:00
/***** Buttons *****/
/* Start buttons container */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTONS_CONTAINER \" > " ) ;
/* Left button */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTON_LEFT_CONTAINER \" > "
" </div> " ) ;
/* Right button */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BUTTON_RIGHT_CONTAINER \" > " ) ;
2019-08-01 18:48:38 +02:00
if ( Match - > Status . QstInd > = Gam_AFTER_LAST_QUESTION ) // Finished
2019-07-18 22:48:27 +02:00
/* Put button to close browser tab */
Gam_PutBigButtonClose ( ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
/* End buttons container */
fprintf ( Gbl . F . Out , " </div> " ) ;
2019-07-18 15:16:36 +02:00
2019-07-31 15:31:02 +02:00
/***** Number of players *****/
Gam_ShowNumPlayers ( Match ) ;
2019-07-25 10:45:36 +02:00
/***** End left container *****/
fprintf ( Gbl . F . Out , " </div> " ) ;
}
/*****************************************************************************/
2019-07-31 15:31:02 +02:00
/************************** Show number of players ***************************/
2019-07-25 10:45:36 +02:00
/*****************************************************************************/
2019-07-31 15:31:02 +02:00
static void Gam_ShowNumPlayers ( struct Match * Match )
2019-07-25 10:45:36 +02:00
{
extern const char * Txt_Players ;
2019-07-31 15:31:02 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_NUM_PLAYERS \" > "
" %s<br /> "
" <strong>%u</strong> "
" </div> " ,
Txt_Players , Match - > Status . NumPlayers ) ;
}
/*****************************************************************************/
/******************** Show match title and close button **********************/
/*****************************************************************************/
static void Gam_ShowMatchTitleAndCloseButton ( struct Match * Match )
{
extern const char * Txt_Close ;
2019-07-25 10:45:36 +02:00
/***** Start container *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_TOP \" > " ) ;
/***** Left: Match title *****/
fprintf ( Gbl . F . Out , " <div class= \" MATCH_TOP_LEFT \" > "
" %s "
" </div> " ,
Match - > Title ) ;
2019-07-31 15:31:02 +02:00
/***** Right: Icon to close this tab *****/
/* onmousedown instead of default onclick
is necessary in order to be fast
and not lose clicks due to refresh */
fprintf ( Gbl . F . Out , " <div class= \" MATCH_TOP_RIGHT ICO_HIGHLIGHT \" > "
" <a href= \" \" title= \" %s \" "
" onmousedown= \" window.close(); \" \" > "
" <img src= \" %s/close.svg \" alt= \" %s \" title= \" %s \" "
" class= \" ICO16x16 \" /> "
" </a> "
" </div> " ,
Txt_Close ,
Cfg_URL_ICON_PUBLIC ,
Txt_Close , Txt_Close ) ;
2019-07-25 10:45:36 +02:00
/***** End container *****/
2019-07-18 15:16:36 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
}
/*****************************************************************************/
/***** Show question and its answers when playing a match (as a teacher) *****/
/*****************************************************************************/
static void Gam_ShowQuestionAndAnswersTch ( struct Match * Match )
{
2019-05-20 19:47:17 +02:00
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
2019-07-17 11:31:42 +02:00
/***** Trivial check: Question index should be > 0 *****/
if ( Match - > Status . QstInd = = 0 )
return ;
2017-09-13 21:22:52 +02:00
2019-07-17 11:31:42 +02:00
/***** Get data of question from database *****/
if ( ! DB_QuerySELECT ( & mysql_res , " can not get data of a question " ,
" SELECT AnsType, " // row[0]
" Stem, " // row[1]
" MedCod " // row[2]
" FROM tst_questions "
" WHERE QstCod=%ld " ,
Match - > Status . QstCod ) )
Ale_ShowAlert ( Ale_ERROR , " Question doesn't exist. " ) ;
row = mysql_fetch_row ( mysql_res ) ;
2017-09-13 21:22:52 +02:00
2019-07-17 11:31:42 +02:00
/***** Show question *****/
/* Get answer type (row[0]) */
Gbl . Test . AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp ( row [ 0 ] ) ;
// TODO: Check that answer type is correct (unique choice)
2019-03-18 15:42:22 +01:00
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " <div class= \" MATCH_BOTTOM \" > " ) ;
2019-07-17 11:31:42 +02:00
/* Write stem (row[1]) */
2019-07-17 22:45:20 +02:00
Tst_WriteQstStem ( row [ 1 ] , " MATCH_TCH_QST " ) ;
2019-03-18 15:42:22 +01:00
2019-07-17 11:31:42 +02:00
/* Get media (row[2]) */
Gbl . Test . Media . MedCod = Str_ConvertStrCodToLongCod ( row [ 2 ] ) ;
Med_GetMediaDataByCod ( & Gbl . Test . Media ) ;
/* Show media */
Med_ShowMedia ( & Gbl . Test . Media ,
" TEST_MED_EDIT_LIST_STEM_CONTAINER " ,
" TEST_MED_EDIT_LIST_STEM " ) ;
/* Write answers? */
if ( Match - > Status . ShowingAnswers )
/* Write answers */
Tst_WriteAnswersGameResult ( Match - > GamCod ,
Match - > Status . QstInd ,
Match - > Status . QstCod ,
2019-07-17 22:45:20 +02:00
" MATCH_TCH_QST " , false ) ; // Don't show result
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
2017-09-13 22:17:27 +02:00
}
2019-07-18 22:48:27 +02:00
/*****************************************************************************/
/***** Show question and its answers when playing a match (as a student) *****/
/*****************************************************************************/
static void Gam_ShowQuestionAndAnswersStd ( struct Match * Match )
{
bool Shuffle = false ; // TODO: Read shuffle from question
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
int StdAnsInd ;
unsigned NumOptions ;
unsigned NumOpt ;
unsigned Index ;
bool ErrorInIndex = false ;
/***** Show question *****/
/* Write buttons for answers? */
if ( Match - > Status . ShowingAnswers )
{
if ( Tst_CheckIfQuestionIsValidForGame ( Match - > Status . QstCod ) )
{
/***** Get student's answer to this question
( < 0 = = > no answer ) * * * * */
StdAnsInd = Gam_GetQstAnsFromDB ( Match - > MchCod ,
Match - > Status . QstInd ) ;
/***** Get number of options in this question *****/
NumOptions = Tst_GetNumAnswersQst ( Match - > Status . QstCod ) ;
/***** Get answers of question from database *****/
Shuffle = false ;
NumOptions = Tst_GetAnswersQst ( Match - > Status . QstCod , & mysql_res , Shuffle ) ;
/*
row [ 0 ] AnsInd
row [ 1 ] Answer
row [ 2 ] Feedback
row [ 3 ] MedCod
row [ 4 ] Correct
*/
/***** Start table *****/
Tbl_StartTableWide ( 8 ) ;
for ( NumOpt = 0 ;
NumOpt < NumOptions ;
NumOpt + + )
{
/***** Get next answer *****/
row = mysql_fetch_row ( mysql_res ) ;
/***** Assign index (row[0]).
Index is 0 , 1 , 2 , 3. . . if no shuffle
or 1 , 3 , 0 , 2. . . ( example ) if shuffle * * * * */
if ( sscanf ( row [ 0 ] , " %u " , & Index ) = = 1 )
{
if ( Index > = Tst_MAX_OPTIONS_PER_QUESTION )
ErrorInIndex = true ;
}
else
ErrorInIndex = true ;
if ( ErrorInIndex )
Lay_ShowErrorAndExit ( " Wrong index of answer when showing a test. " ) ;
/***** Start row *****/
// if (NumOpt % 2 == 0)
fprintf ( Gbl . F . Out , " <tr> " ) ;
/***** Write letter for this option *****/
/* Start table cell */
fprintf ( Gbl . F . Out , " <td class= \" MATCH_STD_CELL \" > " ) ;
/* Form with button.
Sumitting onmousedown instead of default onclick
is necessary in order to be fast
and not lose clicks due to refresh */
Frm_StartForm ( ActAnsMchQstStd ) ;
Gam_PutParamMatchCod ( Match - > MchCod ) ; // Current match being played
Gam_PutParamQstInd ( Match - > Status . QstInd ) ; // Current question index shown
Gam_PutParamAnswer ( Index ) ; // Index for this option
fprintf ( Gbl . F . Out , " <button type= \" submit \" "
" onmousedown= \" document.getElementById('%s').submit(); "
" return false; \" class= \" " ,
Gbl . Form . Id ) ;
if ( StdAnsInd = = ( int ) NumOpt ) // Student's answer
fprintf ( Gbl . F . Out , " MATCH_STD_ANSWER_SELECTED " ) ;
fprintf ( Gbl . F . Out , " MATCH_STD_BUTTON BT_%c \" > "
" %c "
" </button> " ,
' A ' + ( char ) NumOpt ,
' a ' + ( char ) NumOpt ) ;
Frm_EndForm ( ) ;
/* End table cell */
fprintf ( Gbl . F . Out , " </td> " ) ;
/***** End row *****/
// if (NumOpt % 2 == 1)
fprintf ( Gbl . F . Out , " </tr> " ) ;
}
/***** End table *****/
Tbl_EndTable ( ) ;
}
else
Ale_ShowAlert ( Ale_ERROR , " Type of answer not valid in a game. " ) ;
}
}
2017-09-13 22:17:27 +02:00
/*****************************************************************************/
2019-07-18 22:10:54 +02:00
/*********************** Put a big button to do action ***********************/
2017-09-13 22:17:27 +02:00
/*****************************************************************************/
2019-07-17 11:31:42 +02:00
static void Gam_PutBigButton ( Act_Action_t NextAction , long MchCod ,
const char * Icon , const char * Txt )
2017-09-13 22:17:27 +02:00
{
/***** Start form *****/
2019-07-17 11:31:42 +02:00
Frm_StartForm ( NextAction ) ;
2019-05-28 15:06:53 +02:00
Gam_PutParamMatchCod ( MchCod ) ;
2017-09-13 22:17:27 +02:00
/***** Put icon with link *****/
2019-07-18 22:10:54 +02:00
/* Submitting onmousedown instead of default onclick
is necessary in order to be fast
and not lose clicks due to refresh */
fprintf ( Gbl . F . Out , " <a href= \" \" " ) ;
fprintf ( Gbl . F . Out , " title= \" %s \" " , Txt ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " class= \" %s \" " , " MATCH_BUTTON ICO_HIGHLIGHT " ) ;
2019-07-18 22:10:54 +02:00
fprintf ( Gbl . F . Out , " onmousedown= \" " ) ;
fprintf ( Gbl . F . Out , " document.getElementById('%s').submit(); "
" return false; \" > " ,
Gbl . Form . Id ) ;
2019-07-04 21:39:30 +02:00
fprintf ( Gbl . F . Out , " <img src= \" %s/%s \" "
2017-09-14 02:32:36 +02:00
" alt= \" %s \" title= \" %s \" class= \" ICO64x64 \" /> "
2017-09-13 22:17:27 +02:00
" <br /> "
" %s " ,
2019-07-04 21:39:30 +02:00
Cfg_URL_ICON_PUBLIC , Icon ,
Txt , Txt ,
Txt ) ;
2019-05-20 19:47:17 +02:00
fprintf ( Gbl . F . Out , " </a> " ) ;
/***** End form *****/
Frm_EndForm ( ) ;
2019-07-18 22:10:54 +02:00
}
2019-05-20 19:47:17 +02:00
2019-07-18 22:10:54 +02:00
static void Gam_PutBigButtonClose ( void )
{
extern const char * Txt_Close ;
/***** Put icon with link *****/
/* onmousedown instead of default onclick
is necessary in order to be fast
and not lose clicks due to refresh */
fprintf ( Gbl . F . Out , " <a href= \" \" " ) ;
fprintf ( Gbl . F . Out , " title= \" %s \" " , Txt_Close ) ;
2019-07-30 15:21:29 +02:00
fprintf ( Gbl . F . Out , " class= \" %s \" " , " MATCH_BUTTON ICO_HIGHLIGHT " ) ;
2019-07-18 22:10:54 +02:00
fprintf ( Gbl . F . Out , " onmousedown= \" window.close(); \" \" > " ) ;
fprintf ( Gbl . F . Out , " <img src= \" %s/close.svg \" "
" alt= \" %s \" title= \" %s \" class= \" ICO64x64 \" /> "
" <br /> "
" %s " ,
Cfg_URL_ICON_PUBLIC ,
Txt_Close , Txt_Close ,
Txt_Close ) ;
fprintf ( Gbl . F . Out , " </a> " ) ;
2017-09-13 21:22:52 +02:00
}
2019-07-16 22:33:28 +02:00
/*****************************************************************************/
2019-07-18 22:10:54 +02:00
/**************************** Remove old players *****************************/
2019-07-16 22:33:28 +02:00
/*****************************************************************************/
static void Gam_RemoveOldPlayers ( void )
{
2019-07-17 18:17:44 +02:00
/***** Delete matches not being played *****/
DB_QueryDELETE ( " can not update matches as not being played " ,
" DELETE FROM gam_mch_being_played "
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu) " ,
Cfg_SECONDS_TO_REFRESH_GAME * 3 ) ;
/***** Delete players who have left matches *****/
DB_QueryDELETE ( " can not update match players " ,
" DELETE FROM gam_players "
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu) " ,
Cfg_SECONDS_TO_REFRESH_GAME * 3 ) ;
2019-07-16 22:33:28 +02:00
}
2019-07-17 18:17:44 +02:00
static void Gam_UpdateMatchAsBeingPlayed ( long MchCod )
{
2019-07-30 15:21:29 +02:00
/***** Insert match as being played *****/
2019-07-17 18:17:44 +02:00
DB_QueryREPLACE ( " can not set match as being played " ,
" REPLACE gam_mch_being_played (MchCod) VALUE (%ld) " ,
MchCod ) ;
}
static void Gam_SetMatchAsNotBeingPlayed ( long MchCod )
2019-07-16 22:33:28 +02:00
{
/***** Delete all match players ******/
2019-07-17 18:17:44 +02:00
DB_QueryDELETE ( " can not update match players " ,
2019-07-16 22:33:28 +02:00
" DELETE FROM gam_players "
" WHERE MchCod=%ld " ,
MchCod ) ;
2019-07-17 18:17:44 +02:00
/***** Delete match as being played ******/
DB_QueryDELETE ( " can not set match as not being played " ,
" DELETE FROM gam_mch_being_played "
" WHERE MchCod=%ld " ,
MchCod ) ;
}
static bool Gam_GetIfMatchIsBeingPlayed ( long MchCod )
{
/***** Get if a match is being played or not *****/
return
( bool ) ( DB_QueryCOUNT ( " can not get if match is being played " ,
" SELECT COUNT(*) FROM gam_mch_being_played "
" WHERE MchCod=%ld " ,
MchCod ) ! = 0 ) ;
2019-07-16 22:33:28 +02:00
}
static void Gam_RegisterMeAsPlayerInMatch ( long MchCod )
{
/***** Insert me as match player *****/
DB_QueryREPLACE ( " can not insert match player " ,
2019-07-17 18:17:44 +02:00
" REPLACE gam_players (MchCod,UsrCod) VALUES (%ld,%ld) " ,
2019-07-16 22:33:28 +02:00
MchCod , Gbl . Usrs . Me . UsrDat . UsrCod ) ;
}
2019-07-25 10:45:36 +02:00
static void Gam_GetNumPlayers ( struct Match * Match )
2019-07-16 22:33:28 +02:00
{
/***** Get number of players who are playing a match *****/
2019-07-25 10:45:36 +02:00
Match - > Status . NumPlayers = ( unsigned ) DB_QueryCOUNT ( " can not get number of players " ,
" SELECT COUNT(*) FROM gam_players "
" WHERE MchCod=%ld " ,
Match - > MchCod ) ;
2019-07-16 22:33:28 +02:00
}
2019-05-20 12:26:15 +02:00
/*****************************************************************************/
2019-05-31 10:04:13 +02:00
/******************* Show the results of a finished match ********************/
/*****************************************************************************/
void Gam_ShowFinishedMatchResults ( void )
{
Ale_ShowAlert ( Ale_INFO , " To be implemented... " ) ;
}
/*****************************************************************************/
/********************** Get code of match being played ***********************/
2019-05-20 12:26:15 +02:00
/*****************************************************************************/
2019-05-28 15:06:53 +02:00
void Gam_GetMatchBeingPlayed ( void )
2019-05-20 12:26:15 +02:00
{
2019-05-28 15:06:53 +02:00
/***** Get match code ****/
if ( ( Gbl . Games . MchCodBeingPlayed = Gam_GetParamMatchCod ( ) ) = = - 1L )
Lay_ShowErrorAndExit ( " Code of match is missing. " ) ;
2019-05-22 09:36:18 +02:00
}
2019-05-20 12:26:15 +02:00
2019-05-22 09:36:18 +02:00
/*****************************************************************************/
/********* Show game being played to me as student in a new window ***********/
/*****************************************************************************/
2019-05-20 12:26:15 +02:00
2019-07-04 21:39:30 +02:00
void Gam_ShowMatchToMeAsStd ( void )
2019-05-22 09:36:18 +02:00
{
2019-05-29 01:14:56 +02:00
struct Match Match ;
2019-07-18 22:48:27 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-05-29 01:14:56 +02:00
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
2019-07-16 22:33:28 +02:00
/***** Show current match status *****/
2019-07-17 22:45:20 +02:00
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
2019-07-04 21:39:30 +02:00
Gam_ShowMatchStatusForStd ( & Match ) ;
2019-05-22 09:36:18 +02:00
fprintf ( Gbl . F . Out , " </div> " ) ;
}
2019-05-20 12:26:15 +02:00
2019-07-16 22:33:28 +02:00
/*****************************************************************************/
2019-07-17 18:17:44 +02:00
/****************** Refresh match for a teacher via AJAX *********************/
2019-07-16 22:33:28 +02:00
/*****************************************************************************/
2019-07-17 18:17:44 +02:00
void Gam_RefreshMatchTch ( void )
2019-07-16 22:33:28 +02:00
{
2019-07-17 18:17:44 +02:00
struct Match Match ;
2019-07-16 22:33:28 +02:00
if ( ! Gbl . Session . IsOpen ) // If session has been closed, do not write anything
return ;
2019-07-18 22:10:54 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-07-17 18:17:44 +02:00
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
2019-07-30 15:21:29 +02:00
/***** Update match status in database *****/
Gam_UpdateMatchStatusInDB ( & Match ) ;
2019-07-18 15:16:36 +02:00
/***** Show current match status *****/
Gam_ShowMatchStatusForTch ( & Match ) ;
2019-07-16 22:33:28 +02:00
}
2019-05-22 09:36:18 +02:00
/*****************************************************************************/
/*************** Refresh current game for a student via AJAX *****************/
/*****************************************************************************/
2019-05-20 12:26:15 +02:00
2019-07-17 18:17:44 +02:00
void Gam_RefreshMatchStd ( void )
2019-05-22 09:36:18 +02:00
{
2019-05-29 01:14:56 +02:00
struct Match Match ;
if ( ! Gbl . Session . IsOpen ) // If session has been closed, do not write anything
return ;
2019-07-18 22:48:27 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-05-29 01:14:56 +02:00
/***** Get data of the match from database *****/
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
Gam_GetDataOfMatchByCod ( & Match ) ;
2019-07-16 22:33:28 +02:00
/***** Show current match status *****/
2019-07-04 21:39:30 +02:00
Gam_ShowMatchStatusForStd ( & Match ) ;
2019-05-22 09:36:18 +02:00
}
2019-05-20 12:26:15 +02:00
2019-07-15 13:43:34 +02:00
/*****************************************************************************/
/**** Receive previous question answer in a match question from database *****/
/*****************************************************************************/
static int Gam_GetQstAnsFromDB ( long MchCod , unsigned QstInd )
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned NumRows ;
int StdAnsInd = - 1 ; // <0 ==> no answer selected
/***** Get student's answer *****/
NumRows = ( unsigned ) DB_QuerySELECT ( & mysql_res , " can not get student's answer to a match question " ,
" SELECT AnsInd FROM gam_answers "
" WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u " ,
MchCod ,
Gbl . Usrs . Me . UsrDat . UsrCod ,
QstInd ) ;
if ( NumRows ) // Answer found...
{
/***** Get answer index *****/
row = mysql_fetch_row ( mysql_res ) ;
if ( sscanf ( row [ 0 ] , " %d " , & StdAnsInd ) ! = 1 )
Lay_ShowErrorAndExit ( " Error when getting student's answer to a match question. " ) ;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return StdAnsInd ;
}
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-07-09 22:44:41 +02:00
/********* Receive question answer from student when playing a match *********/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-07-09 22:44:41 +02:00
void Gam_ReceiveQstAnsFromStd ( void )
2017-07-09 20:31:40 +02:00
{
2019-07-09 22:44:41 +02:00
struct Match Match ;
2019-07-15 12:41:23 +02:00
unsigned QstInd ;
2019-07-15 13:43:34 +02:00
unsigned StdAnsInd ;
int PreviousStdAnsInd ;
2017-07-09 20:31:40 +02:00
2019-07-18 22:48:27 +02:00
/***** Remove old players.
This function must be called before getting match status . * * * * */
Gam_RemoveOldPlayers ( ) ;
2019-07-09 22:44:41 +02:00
/***** Get data of the match from database *****/
2019-07-15 22:08:06 +02:00
Match . MchCod = Gbl . Games . MchCodBeingPlayed ;
2019-07-09 22:44:41 +02:00
Gam_GetDataOfMatchByCod ( & Match ) ;
2017-07-09 20:31:40 +02:00
2019-07-09 22:44:41 +02:00
/***** Get question index from form *****/
2019-07-15 12:41:23 +02:00
QstInd = Gam_GetParamQstInd ( ) ;
2019-07-09 22:44:41 +02:00
/***** Check that question index is the current one being played *****/
2019-07-15 12:41:23 +02:00
if ( QstInd = = Match . Status . QstInd ) // Receiving an answer
// to the current question being played
2017-07-09 20:31:40 +02:00
{
2019-07-15 12:41:23 +02:00
/***** Get answer index *****/
/*-------+--------------+
| Button | Answer index |
+ - - - - - - - - + - - - - - - - - - - - - - - +
| a | 0 |
| b | 1 |
| c | 2 |
| d | 3 |
| . . . | . . . |
+ - - - - - - - - + - - - - - - - - - - - - - */
2019-07-15 13:43:34 +02:00
StdAnsInd = Gam_GetParamAnswer ( ) ;
/***** Get previous student's answer to this question
( < 0 = = > no answer ) * * * * */
PreviousStdAnsInd = Gam_GetQstAnsFromDB ( Match . MchCod , QstInd ) ;
2019-07-15 12:41:23 +02:00
/***** Store student's answer *****/
2019-07-15 13:43:34 +02:00
if ( PreviousStdAnsInd = = ( int ) StdAnsInd )
DB_QueryDELETE ( " can not register your answer to the match question " ,
" DELETE FROM gam_answers "
" WHERE MchCod=%ld AND UsrCod=%ld AND QstInd=%u " ,
Match . MchCod , Gbl . Usrs . Me . UsrDat . UsrCod , QstInd ) ;
else
DB_QueryREPLACE ( " can not register your answer to the match question " ,
" REPLACE gam_answers "
" (MchCod,UsrCod,QstInd,AnsInd) "
" VALUES "
" (%ld,%ld,%u,%u) " ,
Match . MchCod , Gbl . Usrs . Me . UsrDat . UsrCod , QstInd , StdAnsInd ) ;
2017-07-09 20:31:40 +02:00
}
2019-07-15 22:08:06 +02:00
2019-07-16 22:33:28 +02:00
/***** Show current match status *****/
2019-07-17 22:45:20 +02:00
fprintf ( Gbl . F . Out , " <div id= \" game \" class= \" MATCH_CONT \" > " ) ;
2019-07-15 22:08:06 +02:00
Gam_ShowMatchStatusForStd ( & Match ) ;
fprintf ( Gbl . F . Out , " </div> " ) ;
2017-07-09 20:31:40 +02:00
}
/*****************************************************************************/
2017-09-01 10:37:00 +02:00
/********************* Get number of courses with games **********************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-17 18:22:36 +02:00
// Returns the number of courses with games in this location
2017-07-09 20:31:40 +02:00
2019-04-03 20:57:04 +02:00
unsigned Gam_GetNumCoursesWithGames ( Hie_Level_t Scope )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned NumCourses ;
/***** Get number of courses with games from database *****/
switch ( Scope )
{
2019-04-03 20:57:04 +02:00
case Hie_SYS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" SELECT COUNT(DISTINCT Cod) "
" FROM games "
" WHERE Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTY :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" SELECT COUNT(DISTINCT games.Cod) "
" FROM institutions,centres,degrees,courses,games "
" WHERE institutions.CtyCod=%ld "
" AND institutions.InsCod=centres.InsCod "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ins . InsCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_INS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" SELECT COUNT(DISTINCT games.Cod) "
" FROM centres,degrees,courses,games "
" WHERE centres.InsCod=%ld "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ins . InsCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTR :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" FROM degrees,courses,games "
" WHERE degrees.CtrCod=%ld "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ctr . CtrCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_DEG :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" SELECT COUNT(DISTINCT games.Cod) "
" FROM courses,games "
" WHERE courses.DegCod=%ld "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Deg . DegCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CRS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of courses with games " ,
" SELECT COUNT(DISTINCT Cod) "
" FROM games "
" WHERE Scope='%s' AND Cod=%ld " ,
2019-04-03 20:57:04 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) ,
2019-04-04 10:45:15 +02:00
Gbl . Hierarchy . Crs . CrsCod ) ;
2017-07-09 20:31:40 +02:00
break ;
default :
2018-10-24 23:03:11 +02:00
Lay_WrongScopeExit ( ) ;
2017-07-09 20:31:40 +02:00
break ;
}
/***** Get number of games *****/
row = mysql_fetch_row ( mysql_res ) ;
if ( sscanf ( row [ 0 ] , " %u " , & NumCourses ) ! = 1 )
Lay_ShowErrorAndExit ( " Error when getting number of courses with games. " ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return NumCourses ;
}
/*****************************************************************************/
2017-09-17 18:22:36 +02:00
/**************************** Get number of games ****************************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2017-09-17 18:22:36 +02:00
// Returns the number of games in this location
2017-07-09 20:31:40 +02:00
2019-04-03 20:57:04 +02:00
unsigned Gam_GetNumGames ( Hie_Level_t Scope )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
unsigned NumGames ;
/***** Get number of games from database *****/
switch ( Scope )
{
2019-04-03 20:57:04 +02:00
case Hie_SYS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM games "
" WHERE Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTY :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM institutions,centres,degrees,courses,games "
" WHERE institutions.CtyCod=%ld "
" AND institutions.InsCod=centres.InsCod "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Cty . CtyCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_INS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM centres,degrees,courses,games "
" WHERE centres.InsCod=%ld "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ins . InsCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTR :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM degrees,courses,games "
" WHERE degrees.CtrCod=%ld "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ctr . CtrCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_DEG :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM courses,games "
" WHERE courses.DegCod=%ld "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Deg . DegCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CRS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of games " ,
" SELECT COUNT(*) "
" FROM games "
" WHERE games.Scope='%s' "
" AND CrsCod=%ld " ,
2019-04-03 20:57:04 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) ,
2019-04-04 10:45:15 +02:00
Gbl . Hierarchy . Crs . CrsCod ) ;
2017-07-09 20:31:40 +02:00
break ;
default :
2018-10-24 23:03:11 +02:00
Lay_WrongScopeExit ( ) ;
2017-07-09 20:31:40 +02:00
break ;
}
/***** Get number of games *****/
row = mysql_fetch_row ( mysql_res ) ;
if ( sscanf ( row [ 0 ] , " %u " , & NumGames ) ! = 1 )
Lay_ShowErrorAndExit ( " Error when getting number of games. " ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return NumGames ;
}
/*****************************************************************************/
2017-09-01 10:37:00 +02:00
/************* Get average number of questions per course game ***************/
2017-07-09 20:31:40 +02:00
/*****************************************************************************/
2019-04-03 20:57:04 +02:00
float Gam_GetNumQstsPerCrsGame ( Hie_Level_t Scope )
2017-07-09 20:31:40 +02:00
{
MYSQL_RES * mysql_res ;
MYSQL_ROW row ;
float NumQstsPerGame ;
/***** Get number of questions per game from database *****/
switch ( Scope )
{
2019-04-03 20:57:04 +02:00
case Hie_SYS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM games,gam_questions "
" WHERE games.Scope='%s' "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-03 20:57:04 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTY :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM institutions,centres,degrees,courses,games,gam_questions "
" WHERE institutions.CtyCod=%ld "
" AND institutions.InsCod=centres.InsCod "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Cty . CtyCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_INS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM centres,degrees,courses,games,gam_questions "
" WHERE centres.InsCod=%ld "
" AND centres.CtrCod=degrees.CtrCod "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ins . InsCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CTR :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM degrees,courses,games,gam_questions "
" WHERE degrees.CtrCod=%ld "
" AND degrees.DegCod=courses.DegCod "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Ctr . CtrCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_DEG :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM courses,games,gam_questions "
" WHERE courses.DegCod=%ld "
" AND courses.CrsCod=games.Cod "
" AND games.Scope='%s' "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-03 20:57:04 +02:00
Gbl . Hierarchy . Deg . DegCod ,
Sco_GetDBStrFromScope ( Hie_CRS ) ) ;
2017-07-09 20:31:40 +02:00
break ;
2019-04-03 20:57:04 +02:00
case Hie_CRS :
2018-10-31 10:19:01 +01:00
DB_QuerySELECT ( & mysql_res , " can not get number of questions per game " ,
" SELECT AVG(NumQsts) FROM "
" (SELECT COUNT(gam_questions.QstCod) AS NumQsts "
" FROM games,gam_questions "
" WHERE games.Scope='%s' AND games.Cod=%ld "
" AND games.GamCod=gam_questions.GamCod "
" GROUP BY gam_questions.GamCod) AS NumQstsTable " ,
2019-04-04 10:45:15 +02:00
Sco_GetDBStrFromScope ( Hie_CRS ) , Gbl . Hierarchy . Crs . CrsCod ) ;
2017-07-09 20:31:40 +02:00
break ;
default :
2018-10-24 23:03:11 +02:00
Lay_WrongScopeExit ( ) ;
2017-07-09 20:31:40 +02:00
break ;
}
/***** Get number of courses *****/
row = mysql_fetch_row ( mysql_res ) ;
NumQstsPerGame = Str_GetFloatNumFromStr ( row [ 0 ] ) ;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult ( & mysql_res ) ;
return NumQstsPerGame ;
}