.
/**
 * Library of functions and constants for module game
 *
 * @author 
 * @version $Id: lib.php,v 1.36 2012/07/25 11:16:03 bdaloukas Exp $
 * @package game
 **/
// Define CONSTANTS.
/*
 * Options determining how the grades from individual attempts are combined to give
 * the overall grade for a user
 */
define('GAME_GRADEHIGHEST', 1);
define('GAME_GRADEAVERAGE', 2);
define('GAME_ATTEMPTFIRST', 3);
define('GAME_ATTEMPTLAST', 4);
// The different review options are stored in the bits of $game->review.
// These constants help to extract the options.
define('GAME_REVIEW_IMMEDIATELY', 0x3f);    // The first 6 bits refer to the time immediately after the attempt.
define('GAME_REVIEW_OPEN', 0xfc0);          // The next 6 bits refer to the time after the attempt but while the game is open.
define('GAME_REVIEW_CLOSED', 0x3f000);      // The final 6 bits refer to the time after the game closes.
// Within each group of 6 bits we determine what should be shown.
define('GAME_REVIEW_RESPONSES',   1 * 0x1041); // Show responses.
define('GAME_REVIEW_SCORES',      2 * 0x1041); // Show scores.
define('GAME_REVIEW_FEEDBACK',    4 * 0x1041); // Show feedback.
define('GAME_REVIEW_ANSWERS',     8 * 0x1041); // Show correct answers.
// Some handling of worked solutions is already in the code but not yet fully supported.
// and not switched on in the user interface.
define('GAME_REVIEW_SOLUTIONS',  16 * 0x1041);      // Show solutions.
define('GAME_REVIEW_GENERALFEEDBACK', 32 * 0x1041); // Show general feedback.
/**
 * Given an object containing all the necessary data, 
 * (defined by the form in mod.html) this function 
 * will create a new instance and return the id number 
 * of the new instance.
 *
 * @param object $instance An object from the form in mod.html
 * @return int The id of the newly inserted game record
 **/
function game_add_instance($game) {
    global $DB;
    $game->timemodified = time();
    game_before_add_or_update( $game);
    // May have to add extra stuff in here.
    $id = $DB->insert_record("game", $game);
    $game = $DB->get_record_select( 'game', "id=$id");
    // Do the processing required after an add or an update.
    game_grade_item_update( $game);
    return $id;
}
/**
 * Given an object containing all the necessary data, 
 * (defined by the form in mod.html) this function 
 * will update an existing instance with new data.
 *
 * @param object $instance An object from the form in mod.html
 * @return boolean Success/Fail
 **/
function game_update_instance($game) {
    global $DB;
    $game->timemodified = time();
    $game->id = $game->instance;
    if (!isset( $game->glossarycategoryid)) {
        $game->glossarycategoryid = 0;
    }
    if (!isset( $game->glossarycategoryid2)) {
        $game->glossarycategoryid2 = 0;
    }
    if ($game->grade == '') {
        $game->grade = 0;
    }
    if (!isset( $game->param1)) {
        $game->param1 = 0;
    }
    if ($game->param1 == '') {
        $game->param1 = 0;
    }
    if (!isset( $game->param2)) {
        $game->param2 = 0;
    }
    if ($game->param2 == '') {
        $game->param2 = 0;
    }
    if (!isset( $game->questioncategoryid)) {
        $game->questioncategoryid = 0;
    }
    game_before_add_or_update( $game);
    if (!$DB->update_record("game", $game)) {
        return false;
    }
    // Do the processing required after an add or an update.
    game_grade_item_update( $game);
    return true;
}
function game_before_add_or_update(&$game) {
    if (isset( $game->questioncategoryid)) {
        $pos = strpos( $game->questioncategoryid, ',');
        if ($pos != false) {
            $game->questioncategoryid = substr( $game->questioncategoryid, 0, $pos);
        }
    }
    if ($game->gamekind == 'millionaire') {
        $pos = strpos( '-'.$game->param8, '#');
        if ($pos > 0) {
            $game->param8 = hexdec(substr( $game->param8, $pos));
        }
    } else if ($game->gamekind == 'snakes') {
        $s = '';
        if ($game->param3 == 0) {
            // Means user defined.
            $draftitemid = $game->param4;
            if (isset( $game->id)) {
                $cmg = get_coursemodule_from_instance('game', $game->id, $game->course);
                $modcontext = game_get_context_module_instance( $cmg->id);
                $attachmentoptions = array('subdirs' => 0, 'maxbytes' => 9999999, 'maxfiles' => 1);
                file_save_draft_area_files($draftitemid, $modcontext->id, 'mod_game', 'snakes_file', $game->id,
                    array('subdirs' => 0, 'maxbytes' => 9999999, 'maxfiles' => 1));
                $game->param5 = 1;
            }
            if (isset( $_POST[ 'snakes_cols'])) {
                $fields = array( 'snakes_data', 'snakes_cols', 'snakes_rows', 'snakes_headerx', 'snakes_headery',
                    'snakes_footerx', 'snakes_footery', 'snakes_width', 'snakes_height');
                foreach ($fields as $f) {
                    $s .= '#'.$f.':'.$_POST[ $f];
                }
                $s = substr( $s, 1);
            }
        }
        $game->param9 = $s;
    }
}
/**
 * Given an ID of an instance of this module, 
 * this function will permanently delete the instance 
 * and any data that depends on it. 
 *
 * @param int $id Id of the module instance
 * @return boolean Success/Failure
 **/
function game_delete_instance($gameid) {
    global $DB;
    // Delete any dependent records here.
    $aids = array();
    if (($recs = $DB->get_records( 'game_attempts', array( 'gameid' => $gameid))) != false) {
        $ids = '';
        $count = 0;
        foreach ($recs as $rec) {
            $ids .= ( $ids == '' ? $rec->id : ','.$rec->id);
            if (++$count > 10) {
                $aids[] = $ids;
                $count = 0;
                $ids = '';
            }
        }
        if ($ids != '') {
            $aids[] = $ids;
        }
    }
    foreach ($aids as $ids) {
        $tables = array( 'game_hangman', 'game_cross', 'game_cryptex', 'game_millionaire',
            'game_bookquiz', 'game_sudoku', 'game_snakes');
        foreach ($tables as $t) {
            $sql = "DELETE FROM {".$t."} WHERE id IN (".$ids.')';
            if (!$DB->execute( $sql)) {
                return false;
            }
        }
    }
    $tables = array( 'game_attempts', 'game_grades', 'game_bookquiz_questions', 'game_queries', 'game_repetitions');
    foreach ($tables as $t) {
        if (!$DB->delete_records( $t, array( 'gameid' => $gameid))) {
            return false;
        }
    }
    $tables = array( 'game_export_javame', 'game_export_html', 'game');
    foreach ($tables as $table) {
        if (!$DB->delete_records( $table, array( 'id' => $gameid))) {
            return false;
        }
    }
    return true;
}
/**
 * Return a small object with summary information about what a 
 * user has done with a given particular instance of this module
 * Used for user activity reports.
 * $return->time = the time they did it
 * $return->info = a short text description
 **/
function game_user_outline($course, $user, $mod, $game) {
    global $DB;
    if ($grade = $DB->get_record_select('game_grades', "userid=$user->id AND gameid = $game->id", null, 'id,score,timemodified')) {
        $result = new stdClass;
        if ((float)$grade->score) {
            $result->info = get_string('grade').': '.round($grade->score * $game->grade, $game->decimalpoints).' '.
                            get_string('percent', 'game').': '.round(100 * $grade->score, $game->decimalpoints).' %';
        }
        $result->time = $grade->timemodified;
        return $result;
    }
    return null;
}
/**
 * Print a detailed representation of what a user has done with 
 * a given particular instance of this module, for user activity reports.
 **/
function game_user_complete($course, $user, $mod, $game) {
    global $DB;
    if ($attempts = $DB->get_records_select('game_attempts', "userid='$user->id' AND gameid='$game->id'", null, 'attempt ASC')) {
        if ($game->grade && $grade = $DB->get_record('game_grades', array( 'userid' => $user->id, 'gameid' => $game->id))) {
            echo get_string('grade').': '.game_format_score( $game, $grade->score).'/'.$game->grade.'
';
        }
        foreach ($attempts as $attempt) {
            echo get_string('attempt', 'game').' '.$attempt->attempt.': ';
            if ($attempt->timefinish == 0) {
                print_string( 'unfinished');
            } else {
                echo game_format_score( $game, $attempt->score).'/'.$game->grade;
            }
            echo ' - '.userdate($attempt->timelastattempt).'
';
        }
    } else {
        print_string('noattempts', 'game');
    }
    return true;
}
/**
 * Given a course and a time, this module should find recent activity 
 * that has occurred in game activities and print it out. 
 * Return true if there was output, or false is there was none. 
 *
 * @uses $CFG
 * @return boolean
 * @todo Finish documenting this function
 **/
function game_print_recent_activity($course, $isteacher, $timestart) {
    global $CFG;
    return false;  // True if anything was printed, otherwise false.
}
/**
 * Function to be run periodically according to the moodle cron
 * This function searches for things that need to be done, such 
 * as sending out mail, toggling flags etc ... 
 *
 * @uses $CFG
 * @return boolean
 * @todo Finish documenting this function
 **/
function game_cron() {
    global $CFG;
    return true;
}
/**
 * Must return an array of grades for a given instance of this module, 
 * indexed by user.  It also returns a maximum allowed grade.
 * 
 * Example:
 *    $return->grades = array of grades;
 *    $return->maxgrade = maximum allowed grade;
 *
 *    return $return;
 *
 * @param int $gameid ID of an instance of this module
 * @return mixed Null or object with an array of grades and with the maximum grade
 **/
function game_grades($gameid) {
    // Must return an array of grades, indexed by user, and a max grade.
    global $DB;
    $game = $DB->get_record( 'game', array( 'id' => intval($gameid)));
    if (empty($game) || empty($game->grade)) {
        return null;
    }
    $return = new stdClass;
    $return->grades = $DB->get_records_menu('game_grades', 'gameid', $game->id, '', "userid, score * {$game->grade}");
    $return->maxgrade = $game->grade;
    return $return;
}
/**
 * Return grade for given user or all users.
 *
 * @param int $gameid id of game
 * @param int $userid optional user id, 0 means all users
 * @return array array of grades, false if none
 */
function game_get_user_grades($game, $userid=0) {
    global $DB;
    $user = $userid ? "AND u.id = $userid" : "";
    $sql = 'SELECT u.id, u.id AS userid, '.$game->grade.
            ' * g.score AS rawgrade, g.timemodified AS dategraded, MAX(a.timefinish) AS datesubmitted
            FROM {user} u, {game_grades} g, {game_attempts} a
            WHERE u.id = g.userid AND g.gameid = '.$game->id.' AND a.gameid = g.gameid AND u.id = a.userid';
    if ($userid != 0) {
        $sql .= ' AND u.id='.$userid;
    }
    $sql .= ' GROUP BY u.id, g.score, g.timemodified';
    return $DB->get_records_sql( $sql);
}
/**
 * Must return an array of user records (all data) who are participants
 * for a given instance of game. Must include every user involved
 * in the instance, independient of his role (student, teacher, admin...)
 * See other modules as example.
 *
 * @param int $gameid ID of an instance of this module
 * @return mixed boolean/array of students
 **/
function game_get_participants($gameid) {
    return false;
}
/**
 * This function returns if a scale is being used by one game
 * it it has support for grading and scales. Commented code should be
 * modified if necessary. See forum, glossary or journal modules
 * as reference.
 *
 * @param int $gameid ID of an instance of this module
 * @return mixed
 * @todo Finish documenting this function
 **/
function game_scale_used ($gameid, $scaleid) {
    $return = false;
    return $return;
}
/**
 * Update grades in central gradebook
 *
 * @param object $game null means all games
 * @param int $userid specific user only, 0 mean all
 */
function game_update_grades($game=null, $userid=0, $nullifnone=true) {
    global $CFG;
    if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.
        if (file_exists( $CFG->libdir.'/gradelib.php')) {
            require_once($CFG->libdir.'/gradelib.php');
        } else {
            return;
        }
    }
    if ($game != null) {
        if ($grades = game_get_user_grades($game, $userid)) {
            game_grade_item_update($game, $grades);
        } else if ($userid and $nullifnone) {
            $grade = new stdClass;
            $grade->userid   = $userid;
            $grade->rawgrade = null;
            game_grade_item_update( $game, $grade);
        } else {
            game_grade_item_update( $game);
        }
    } else {
        $sql = "SELECT a.*, cm.idnumber as cmidnumber, a.course as courseid
                  FROM {game} a, {course_modules} cm, {modules} m
                 WHERE m.name='game' AND m.id=cm.module AND cm.instance=a.id";
        if ($rs = $DB->get_recordset_sql( $sql)) {
            while ($game = $DB->rs_fetch_next_record( $rs)) {
                if ($game->grade != 0) {
                    game_update_grades( $game, 0, false);
                } else {
                    game_grade_item_update( $game);
                }
            }
            $DB->rs_close( $rs);
        }
    }
}
/**
 * Create grade item for given game
 *
 * @param object $game object with extra cmidnumber
 * @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
 * @return int 0 if ok, error code otherwise
 */
function game_grade_item_update($game, $grades=null) {
    global $CFG;
    if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.
        if (file_exists( $CFG->libdir.'/gradelib.php')) {
            require_once($CFG->libdir.'/gradelib.php');
        } else {
            return;
        }
    }
    if (array_key_exists('cmidnumber', $game)) { // Tt may not be always present.
        $params = array('itemname' => $game->name, 'idnumber' => $game->cmidnumber);
    } else {
        $params = array('itemname' => $game->name);
    }
    if ($game->grade > 0) {
        $params['gradetype'] = GRADE_TYPE_VALUE;
        $params['grademax']  = $game->grade;
        $params['grademin']  = 0;
    } else {
        $params['gradetype'] = GRADE_TYPE_NONE;
    }
    if ($grades === 'reset') {
        $params['reset'] = true;
        $grades = null;
    }
    return grade_update('mod/game', $game->course, 'mod', 'game', $game->id, 0, $grades, $params);
}
/**
 * Delete grade item for given game
 *
 * @param object $game object
 * @return object game
 */
function game_grade_item_delete( $game) {
    global $CFG;
    if (file_exists( $CFG->libdir.'/gradelib.php')) {
        require_once($CFG->libdir.'/gradelib.php');
    } else {
        return;
    }
    return grade_update('mod/game', $game->course, 'mod', 'game', $game->id, 0, null, array('deleted' => 1));
}
/**
 * Returns all game graded users since a given time for specified game
 */
function game_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
    global $DB, $COURSE, $USER;
    if ($COURSE->id == $courseid) {
        $course = $COURSE;
    } else {
        $course = $DB->get_record('course', array( 'id' => $courseid));
    }
    $modinfo = get_fast_modinfo($course);
    $cm = $modinfo->cms[$cmid];
    if ($userid) {
        $userselect = "AND u.id = $userid";
    } else {
        $userselect = "";
    }
    if ($groupid) {
        $groupselect = "AND gm.groupid = $groupid";
        $groupjoin   = "JOIN {groups_members} gm ON  gm.userid=u.id";
    } else {
        $groupselect = "";
        $groupjoin   = "";
    }
    if (!$attempts = $DB->get_records_sql("SELECT qa.*, qa.gameid, q.grade, u.lastname,".
            " u.firstname, firstnamephonetic, u.lastnamephonetic, u.middlename, u.alternatename,".
            " u.lastnamephonetic, u.picture
                                        FROM {game_attempts} qa
                                             JOIN {game} q ON q.id = qa.gameid
                                             JOIN {user} u ON u.id = qa.userid
                                             $groupjoin
                                       WHERE qa.timefinish > $timestart AND q.id = $cm->instance
                                             $userselect $groupselect
                                    ORDER BY qa.timefinish ASC")) {
         return;
    }
    $cmcontext      = game_get_context_module_instance( $cm->id);
    $grader          = has_capability('moodle/grade:viewall', $cmcontext);
    $accessallgroups = has_capability('moodle/site:accessallgroups', $cmcontext);
    $viewfullnames   = has_capability('moodle/site:viewfullnames', $cmcontext);
    $groupmode       = groups_get_activity_groupmode($cm, $course);
    if (is_null($modinfo->groups)) {
        $modinfo->groups = groups_get_user_groups($course->id); // Load all my groups and cache it in modinfo.
    }
    $aname = format_string($cm->name, true);
    foreach ($attempts as $attempt) {
        if ($attempt->userid != $USER->id) {
            if (!$grader) {
                // Grade permission required.
                continue;
            }
            if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
                $usersgroups = groups_get_all_groups($course->id, $attempt->userid, $cm->groupingid);
                if (!is_array($usersgroups)) {
                    continue;
                }
                $usersgroups = array_keys($usersgroups);
                $interset = array_intersect($usersgroups, $modinfo->groups[$cm->id]);
                if (empty($intersect)) {
                    continue;
                }
            }
        }
        $tmpactivity = new stdClass;
        $tmpactivity->type      = 'game';
        $tmpactivity->gameid    = $attempt->gameid;
        $tmpactivity->cmid      = $cm->id;
        $tmpactivity->name      = $aname;
        $tmpactivity->sectionnum = $cm->sectionnum;
        $tmpactivity->timestamp = $attempt->timefinish;
        $tmpactivity->content = new stdClass;
        $tmpactivity->content->attemptid = $attempt->id;
        $tmpactivity->content->sumgrades = $attempt->score * $attempt->grade;
        $tmpactivity->content->maxgrade  = $attempt->grade;
        $tmpactivity->content->attempt   = $attempt->attempt;
        $tmpactivity->user = new stdClass;
        $tmpactivity->user->userid   = $tmpactivity->user->id = $attempt->userid;
        $tmpactivity->user->fullname = fullname($attempt, $viewfullnames);
        $tmpactivity->user->firstname = $attempt->firstname;
        $tmpactivity->user->lastname = $attempt->lastname;
        $tmpactivity->user->alternatename = $attempt->alternatename;
        $tmpactivity->user->middlename = $attempt->middlename;
        $tmpactivity->user->firstnamephonetic = $attempt->firstnamephonetic;
        $tmpactivity->user->lastnamephonetic = $attempt->lastnamephonetic;
        $tmpactivity->user->picture  = $attempt->picture;
        $tmpactivity->user->imagealt  = $attempt->imagealt;
        $tmpactivity->user->email  = $attempt->email;
        $activities[$index++] = $tmpactivity;
    }
}
function game_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
    global $CFG, $OUTPUT;
    echo '
| "; echo $OUTPUT->user_picture($activity->user, array('courseid' => $courseid)); echo " | ";
    if ($detail) {
        $modname = $modnames[$activity->type];
        echo ' ';
        echo "';
    }
    echo ' ';
    echo  get_string("attempt", "game")." {$activity->content->attempt}: ";
    $grades = "({$activity->content->sumgrades} / {$activity->content->maxgrade})";
    echo "wwwroot}/mod/game/review.php".
        "?attempt={$activity->content->attemptid}&q={$activity->gameid}\">$grades";
    echo '';
    echo ' ';
    echo "wwwroot}/user/view.php?id={$activity->user->userid}&course=$courseid\">"
         ."{$activity->user->fullname} - ".userdate($activity->timestamp);
    echo '';
    echo " |