Questions from the forum
 Q: I'd like my camera to tilt when the player is going through a slope on a mountain. Can you help? A: Use the snippet below:   action players_code() // attach this action to your player {        var movement_speed = 10; // movement speed        VECTOR temp;        ANGLE temp_angles;        set (my, INVISIBLE); // 1st person player        player = my; // I'm the player        while (1)        {                my.pan -= 7 * mouse_force.x * time_step;                camera.x = my.x;                camera.y = my.y;                camera.z = my.z + 50 + 1.1 * sin(my.skill44); // play with 50 and 1.1                camera.pan = my.pan;                camera.tilt = player.tilt;                vec_set (temp.x, my.x); // trace 10,000 quants below the player                temp.z -= 10000;                temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2; // play with 2                temp.x = movement_speed * (key_w - key_s) * time_step;                temp.y = movement_speed * (key_a - key_d) * 0.6 * time_step;                c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);                temp_angles.tilt = 0;                temp_angles.roll = 0;                temp_angles.pan = -my.pan;                vec_rotate(normal, temp_angles);                temp_angles.tilt = -asin(normal.x);                temp_angles.roll = -asin(normal.y);                my.tilt += 0.1 * ang(temp_angles.tilt - my.tilt); // play with 0.1                my.roll += 0.1 * ang(temp_angles.roll - my.roll); // play with 0.1                wait (1);        } } Q: How can I trap the mouse pointer in a rectangular area located in the center of the screen? A: Use this example:   // trap the mouse in an area that has 200x100 pixels and is located in the center of the screen var area_x = 200; var area_y = 100;   BMAP* pointer_tga = "pointer.tga";   function mouse_startup() {         VECTOR temp1_pos, temp2_pos;        mouse_mode = 2;        mouse_map = pointer_tga;        while (1)        {                 vec_set(mouse_pos, mouse_cursor);                temp1_pos.x = (screen_size.x - area_x) / 2;                temp2_pos.x = (screen_size.x + area_x) / 2;                mouse_pos.x = clamp(mouse_pos.x, temp1_pos.x, temp2_pos.x);                temp1_pos.y = (screen_size.y - area_y) / 2;                temp2_pos.y = (screen_size.y + area_y) / 2;                mouse_pos.y = clamp(mouse_pos.y, temp1_pos.y, temp2_pos.y);                wait(1);        } }     Q: How can I make it so that an entity moves for a specified number of seconds when I press a button? A: There you go:   action moving_puppet() {        var movement_time = 5; // move for 5 seconds when the "M" key is pressed        var movement_speed = 2; // movement speed        VECTOR temp;        while (1)        {                while (!key_m) {wait (1);} // wait until the player presses the "M" key on the keyboard                while (key_m) {wait (1);} // wait until the player releases the "M" key                while (movement_time > 0)                {                        movement_time -= time_step / 16; // decrease 1 from movement_time each second                        my.skill1 += 4 * time_step; // 4 gives the animation speed                        vec_set (temp.x, my.x); // trace 10,000 quants below the entity                        temp.z -= 10000;                        temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 20; // play with 20                        // move the entity with the speed given by movement_speed along the direction pointed by its pan angle                        c_move (my, vector(movement_speed, 0, temp.z), nullvector, IGNORE_PASSABLE | GLIDE);                        // animate the model using its "walk" animation in a loop                        ent_animate(my, "walk", my.skill1, ANM_CYCLE);                        wait (1);                }                // the walking is over here, so let's switch to the "stand" animation                ent_animate(my, "stand", 0, 0);                movement_time = 5; // prepare for a new movement session        } }     Q: How can I make a sound play once as soon as a comparison instruction is successful? A: Here's an example:   STRING* password_str = "iliketoeatalot"; STRING* input_str = "#20"; // this empty string can store up to 20 characters STRING* messages_str = "30";   SOUND* passok_wav = "passok.wav";   TEXT* input_txt = {        pos_x = 300;        pos_y = 400;        string(input_str); }   TEXT* messages_txt = {        pos_x = 300;        pos_y = 300;        string(messages_str); }   function password_startup() {        wait (-3); // wait until the game starts, etc        // the following loop will run until the correct password is typed in        while (1)        {                str_cpy(messages_str, "Type in the correct password");                messages_txt.flags |= SHOW; // show the intro message                wait (-3); // for 3 seconds                messages_txt.flags &= ~SHOW; // now hide the intro message                input_txt.flags |= SHOW; // show the input                str_cpy(input_str, "#20"); // reset the string (if needed)                inkey(input_str);             if (str_cmpi(input_str, password_str) == 1) // the correct password was typed in?              {                        snd_play(passok_wav, 90, 0);                        break; // get out of the loop                }        }        // the correct password was typed in here        str_cpy(messages_str, "Logged into the system");        messages_txt.flags |= SHOW; // show the outro message        wait (-5); // for 5 seconds        messages_txt.flags &= ~SHOW; // now hide the outro message                sys_exit(NULL); // shut down the engine }     Q: I want to attach the camera of my game to an object (sphere, etc) so that the camera has a physical behaviour and doesn't go through walls anymore. A: Use this piece of code:   VECTOR movement_force;   action solid_camera() // attach this action to a sphere model {        set (my, INVISIBLE); // no need to see the sphere model        while (1)        {                // use the W and S keys to move forward / backward, 5 gives the movement speed                movement_force.x = 5 * (key_w - key_s);                // use the A and D keys to move sideways, 3 gives the movement speed                movement_force.y = 3 * (key_a - key_d);                // don't allow the z value of movement_force to change                movement_force.z = 0;                my.pan -= 3 * mouse_force.x * time_step; // 3 = horizontal rotation speed                my.tilt -= 3 * mouse_force.y * time_step; // 3 = vertical rotation speed                // now move the sphere in the direction given by its pan and tilt angles                c_move (my, movement_force.x, nullvector, IGNORE_PASSABLE | GLIDE);                // the camera will inherit the same position and angles with the sphere                vec_set (camera.x, my.x);                camera.pan = my.pan;                camera.tilt = my.tilt;                wait(1);        } }     Q: For my tests I'd need a snippet which, with the press of a button, writes camera's coordinates to an ini file. Then, with the press of another button, the camera would load those coordinates from the ini file. A: There you go:   var file_handle;   VECTOR vSpeed, vAngularSpeed, vForce, vMove;   function camera_startup() // this camera can pass through walls {        while (1)        {                vForce.x = - 5 * (key_force.x + mouse_force.x);        // pan angle                vForce.y = 5 * (key_force.y + mouse_force.y); // tilt angle                vForce.z = 0; // no changes for the roll angle                vec_accelerate (vMove, vAngularSpeed, vForce, 0.8);                vec_add (camera.pan, vMove);                vForce.x = 6 * (key_w - key_s); // use W and S to move forward                vForce.y = 6 * (key_a - key_d); // use A and D to move sideways                vForce.z = 6 * (key_home - key_end); // use Home and End to move upwards / downwards                vec_accelerate (vMove, vSpeed, vForce, 0.5);                vec_rotate (vMove, camera.pan);                vec_add (camera.x, vMove);                wait(1);        } }   function save_position() {        file_handle = file_open_write ("camera.ini"); // save the data to the camera.ini file        file_var_write (file_handle, camera.x); // save x, y, z, pan, tilt, roll        file_var_write (file_handle, camera.y);        file_var_write (file_handle, camera.z);        file_var_write (file_handle, camera.pan);        file_var_write (file_handle, camera.tilt);        file_var_write (file_handle, camera.roll);        file_close (file_handle); // close the file, we won't be needing it from now on }   function load_position() {        file_handle = file_open_read("camera.ini"); // try to open ini file        if (file_handle) // the file exists? (the player could press "2" without pressing "1" first)        {                camera.x = file_var_read (file_handle); // then restore the x, y, z, pan, tilt, roll                camera.y = file_var_read (file_handle);                camera.z = file_var_read (file_handle);                camera.pan = file_var_read (file_handle);                camera.tilt = file_var_read (file_handle);                camera.roll = file_var_read (file_handle);        } }   function init_startup() {        on_1 = save_position; // press "1" to save the camera coordinates        on_2 = load_position; // press "2" to load the camera coordinates }     Q: I'd like to be able to pick, move and drop an object using the mouse cursor. A: Here's an improved version of my code from Aum52's faq that runs fine with A7 / lite-C:   BMAP* pointer_tga = "pointer.tga";   VECTOR temp;   function mouse_startup() {         mouse_mode = 2;        mouse_map = pointer_tga;        while (1)        {                 vec_set(mouse_pos, mouse_cursor);                wait(1);        } }   function pick_or_drop() {        wait (1);        my.skill1 += 1;        if ((my.skill1 % 2) == 1) // clicked the object?        {                while (mouse_left) {wait (1);} // wait until the player releases the left mouse button                while (!mouse_left) // move the object until the player presses the mouse button again                {                        temp.x = mouse_cursor.x;                        temp.y = mouse_cursor.y;                        temp.z = 200; // move the object 200 quants below the camera, play with this value                        vec_for_screen(temp.x, camera);                        vec_set (my.x, temp.x);                        set (my, PASSABLE);                        wait (1);                }        }        else // drop the object here        {                vec_set (temp.x, my.x);                temp.z -= 3000; // trace up to 5,000 quants below the player                // make sure to drop the object on the ground                my.z -= c_trace (my.x, temp.x, IGNORE_ME + IGNORE_SPRITES + IGNORE_MODELS + USE_BOX);                reset (my, PASSABLE);        } }   action click_and_move() {        my.skill1 = 0;        my.emask |= ENABLE_CLICK;        my.event = pick_or_drop; } Q: I have developed a small project where the player has to guess a number from 1 to 9. I can enter the code using the keyboard numbers from 1 to 9 but nothing happens when pressing the numerical keypad numbers. I have the num_lock on. Why does this not work? A: The numerical keypad numbers have other scan codes; use the snippet below to detect the names and the scan codes for the keys on the keyboard. I suggest that you should always use scan codes for your projects.   STRING* temp_str = "#10";   TEXT* header_txt = {        pos_x = 180;        pos_y = 5;        string("Key name ------- Scan code");        flags = SHOW; }   TEXT* keypressed_txt = {        pos_x = 200;        pos_y = 20;        string(temp_str);        flags = SHOW; }   PANEL* keypressed_pan = {        layer = 15;        digits(300, 20, 3, * , 1, key_lastpressed);        flags = SHOW; }   function keys_startup() {        while (1)        {                if (key_any)                {                        beep();                        str_for_key(temp_str, key_lastpressed); // convert the scan code to the key name                }                while (key_any) {wait (1);} // wait until all the keys are released                wait (1);        } }     Q: I'd like to have a dynamic light that can be picked up by pressing space near it and follows the player. The light should always be on, even after a level changing instruction. How can I do that? A: The actions attached to entities stop running after a level_load instruction; nevertheless, the variables keep their values and can be used to recreate what's needed, just like this:   var got_flashlight = 0;   SOUND* gotflashlight_wav = "gotflashlight.wav";   ENTITY* flashlight;   function move_flashlight() {        set (my, PASSABLE | INVISIBLE | CAST);        my.lightrange = 350;        my.red = 200;        my.green = 200;        my.blue = 160;        while (player) // this loop will run for as long as the player pointer exists        {                vec_set(my.x, player.x);                wait (1);        } }   action my_flashlight() {        while (!player) {wait (1);}        while (1)        {                if (vec_dist(player.x, my.x) < 70)                {                        if (key_space)                        {                                snd_play(gotflashlight_wav, 50, 0);                                flashlight = ent_create("flashlight.mdl", player.x, move_flashlight);                                got_flashlight = 1;                                wait (2);                                break; // get out of this loop                        }       }                wait (1);        }        set(my, PASSABLE | INVISIBLE); // hide the flashlight model on the floor        while (vec_dist(player.x, my.x) < 300) {wait (1);} // wait until the player moves away        // and then remove the flashlight model from the floor (give the sound effect enough time to play)        ent_remove (my); }   action players_code() // attach this action to your player model in all the levels {        var movement_speed = 10; // movement speed        VECTOR temp;        set (my, INVISIBLE); // 1st person player        player = my; // I'm the player          // this section recreates the flashlight (if needed)        if (got_flashlight == 1) // the flashlight was picked up in a previous level?        {                flashlight = ent_create("flashlight.mdl", player.x, move_flashlight); // the recreate it!        }        // end of the section that recreates the flashlight (if needed)          while (1)        {                my.pan -= 7 * mouse_force.x * time_step;                camera.x = my.x;                camera.y = my.y;                camera.z = my.z + 50 + 1.1 * sin(my.skill44); // play with 50 and 1.1                camera.pan = my.pan;                camera.tilt = player.tilt; //5 * mouse_force.y * time_step;                vec_set (temp.x, my.x); // trace 10,000 quants below the player                temp.z -= 10000;                temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2; // play with 2                temp.x = movement_speed * (key_w - key_s) * time_step;                temp.y = movement_speed * (key_a - key_d) * 0.6 * time_step;                c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);                wait (1);        } }   action lever_changer() // attach this action to your level changing entity {        while (!player) {wait (1);} // wait until the player model is loaded        // wait until the player comes closer than 100 quants to the level changing entity        while (vec_dist (player.x, my.x) > 100) {wait (1);}        // now load the new level; the flashlight will be recreated if it was picked up in a previous level        level_load("level2.wmb"); }     Q: I'd like to create a mini math quiz with a text like 5 x 2 that appears on the screen. The users enter an answer and press Enter in order to move on to the following question. If they are wrong their score is decreased. A: Use this example as a base for your code:   var score = 0, number1, number2, my_result;   STRING* question_str = "#20"; STRING* answer_str = "#5"; STRING* temp_str = "#5";   SOUND* goodjob_wav = "goodjob.wav"; SOUND* missed_wav = "missed.wav";   TEXT* question_txt = {        pos_x = 200;        pos_y = 200;        string(question_str);        flags = SHOW; }   TEXT* answer_txt = {        pos_x = 300;        pos_y = 200;        string(answer_str);        flags = SHOW; }   PANEL* score_pan = {        layer = 15;        digits(700, 20, 4, * , 1, score);        flags = SHOW; }   function init_startup() {        random_seed(0); // generate a random sequence of numbers        wait (-3); // wait until the level is loaded        while (1)        {                str_cpy(question_str, "What is ");                number1 = integer(random(10) + 1); // number 1 ranges from 1 to 10                str_for_num(temp_str, number1); // convert number1 to a string                str_cat(question_str, temp_str); // add number1 to question_str                str_cat(question_str, " x ");                number2 = integer(random(10) + 1); // number 2 ranges from 1 to 10                str_for_num(temp_str, number2); // convert number1 to a string                str_cat(question_str, temp_str); // add number2 to question_str                wait (1);                                        str_cat(question_str, " = ");                str_cpy(answer_str, "#5");                inkey(answer_str);                my_result = str_to_num(answer_str);                if (my_result == (number1 * number2)) // the player has got the correct answer?                {                        score += 1; // then increase the score                        snd_play(goodjob_wav, 60, 0);                }                else                {                        snd_play(missed_wav, 60, 0);                        score -= 1; // then decrease the score                        score = maxv(0, score); // don't allow negative score values                }                wait (-3); // wait for 5 seconds before generating a new exercise        } }