////////////////////////////////////////////////////////////////////////////
// simple lite-C online game
////////////////////////////////////////////////////////////////////////////
#include <acknex.h>
#include <default.c>
#define health skill20
////////////////////////////////////////////////////////////////////////////
var fired = 0;
var client_ent = 0; // stores the handle to the client entity (client's player)
var server_fired = 0;
var client_fired = 0;
var bullet_pan; // stores the pan angle of the server or client player
var players_health;
var cam_angle = 90; // set the initial camera angle here
var cam_dist = 250; // set the default zoom in factor here
var cam_height = 150; // set the default camera height here
var im_server = 0;
var im_client = 0;
var server_only = 0;
var client_only = 0;
var client_server = 0;
var player_id = 0;
var my_id = 0;
VECTOR temp2;
BMAP* hit_tga = "hit.tga";
BMAP* mouse_tga = "C_Standard_800x600.tga"; // bitmap used for the mouse pointer
STRING* client_test_str ="#100";
FONT* arial_font = "Arial#15";
///////////////////////////////////////////////////////////////////////////
TEXT* client_test_txt = // displays the info messages at the top of the screen
{
pos_x = 10;
pos_y = 30;
layer = 10;
font(arial_font);
string (client_test_str);
flags = visible;
}
/////////////////////////////////////////////////////////////////////////////
function simple_camera()
{
cam_dist += 0.9 * mickey.z * time_step; // zoom in / out using the mouse wheel
cam_dist = clamp(cam_dist, 20, 500); // limit the distance between the camera and the player to 20...500
cam_height+= 8 *(mouse_force.y)* time_step; // set the height of the camera using the mouse
camera.clip_near = 5; // don't pass walls when close to them
cam_height = clamp(cam_height, 5, 300); // limit the height of the camera
camera.x = my.x - cam_dist * cos(cam_angle);
camera.y = my.y - cam_dist * sin(cam_angle);
if(mouse_right == 1)
{
cam_angle += 4 *(mouse_force.x) * time_step;
}
else
{
cam_angle = my.pan; // the camera and the player have the same pan angle
}
camera.z = my.z + cam_height;
vec_set(temp2.x, my.x);
vec_sub(temp2.x, camera.x);
vec_to_angle(camera.pan, temp2); // rotate the camera towards the player at all times
}
function fade_hits(PARTICLE *p)
{
p.alpha -= 4 * time_step; // fade out the hit_tga particles
if (p.alpha < 0)
p.lifespan = 0;
}
function player_hit(PARTICLE *p)
{
p->vel_x = 0.2 - random(1) / 4;
p->vel_y = 0.2 - random(1) / 4;
p->vel_z = 0.3 + random(1) / 3;
p.alpha = 25 + random(50);
p.bmap = hit_tga;
p.size = 5; // gives the size of the hit particles
p.flags |= (BRIGHT | MOVE);
p.event = fade_hits;
}
function hit_effect() // this function will generate particles only on the client
{
effect(player_hit, 5, my.x, nullvector); // generate 5 particles each frame
}
function remove_bullets() // this function runs when the bullet collides with something
{
if (connection == 2) // this section runs on the client?
{
proc_client(my, hit_effect); // then start a function that will only run on the client
}
else // this section runs on the server
{
// under normal conditions (without checking the "connection" value) this would create particles on the client, as well as on the server
effect(player_hit, 5, my.x, nullvector);
}
wait (1); // wait a frame to be sure (don't trigger engine warnings)
ent_remove (my); // and then remove the bullet
}
function move_bullets() // this function runs on the server (any ent_create instruction is run on the server)
{
VECTOR bullet_speed[3]; // stores the speed of the bullet
my.emask = ENABLE_IMPACT | ENABLE_ENTITY | ENABLE_BLOCK; // the bullet is sensitive to impacts with entities and level blocks
my.event = remove_bullets; // when it collides with something, its event function (remove_bullets) will run
my.pan = bullet_pan; // my.pan could be set to server's player.pan or client's player.pan
my.skill99 = 1234; // set a unique identifier for the bullets
bullet_speed.x = 50 * time_step; // adjust the speed of the bullet here
bullet_speed.y = 0; // the bullet doesn't move sideways
bullet_speed.z = 0; // or up / down on the z axis
while (my) // this loop will run for as long as the bullet exists (it isn't "NULL")
{
c_move (my, bullet_speed, nullvector, IGNORE_PASSABLE | IGNORE_YOU); // move the bullet ignoring its creator (the player)
ent_sendnow(my); // send the updated bullet position each frame (ensures smooth bullet movement on the client as well)
wait (1);
}
}
function fire_bullets() // this function is called from main, so it runs on the server and on the client
{
if (connection == 2 ) // this section runs on the client
{
str_cpy(client_test_str, "Client Fired");
fired += 1;
wait(1);
PANEL* digit_pan =
{
digits (80, 35, 2, *, 1, fired);
flags = VISIBLE;
}
client_fired = 1; // we set client_fired for a frame, and then we reset it
wait(1);
client_fired = 0;
}
if (connection == 3 ) // this section runs on the client
{
str_cpy(client_test_str, "Server Fired");
fired += 1;
wait(1);
PANEL* digit_pan =
{
digits (80, 35, 2, *, 1, fired);
flags = VISIBLE;
}
server_fired =1;
wait(1);
server_fired = 0;
}
}
function on_client_event() // terminate client when server disconnects
{
if (event_type == EVENT_LEAVE)
{
sys_exit("Disconnected!");
}
}
function player_remove() // remove player when client disconnects
{
if (event_type == EVENT_DISCONNECT)
{
ent_remove(me);
}
}
function init_mouse()
{
mouse_mode = 2;
mouse_map = mouse_tga;
while (1)
{
vec_set(mouse_pos, mouse_cursor);
wait(1);
}
}
action player_move() // control the player on its client, move it on the server, animate it on all clients
{
my.emask |= ENABLE_DISCONNECT;
my.smask |= NOSEND_FRAME; // don't send animation
my.event = player_remove;
var walk_percentage = 0;
var skill1_old = 0, skill2_old = 0;
VECTOR bullet_pos[3];
my.health = 100;
send_skill(my.health, 0); // send the health value once, at the very beginning, in order to update the health value on the client player at game start
while (my.health > 0) // the server player is still alive?
{
if (my.client_id == dplay_id) // player created by this client?
{
if (key_w-key_s != my.skill1)// forward/backward key state changed?
{
my.skill1 = key_w-key_s;
send_skill(my.skill1,0); // send the key state in reliable mode
}
if (key_a-key_d != my.skill2)// rotation key changed?
{
my.skill2 = key_a-key_d;
send_skill(my.skill2,0); // send rotation state in reliable mode
}
send_var_to(NULL,client_fired); // send client_fired to the server; even client's bullets are created on the server
simple_camera();
}
if (connection == 3) // running on the server?
{
if (my.skill1 != skill1_old)
{ // if movement changed
send_skill(my.skill1,SEND_ALL); // send movement to all clients
skill1_old = my.skill1;
}
if (my.skill2 != skill2_old)
{ // if rotation changed
send_skill(my.skill2,SEND_ALL); // send rotation to all clients
skill2_old = my.skill2;
}
if(server_fired == 1) // the client has fired? Then we need to get access to the client player
{
vec_set (bullet_pos.x, vector (80, -10, 25)); // create the bullet at an offset of x = 30, y = -5, z = 15
vec_rotate (bullet_pos.x, my.pan); // in relation to the "you" model (client's player)
vec_add (bullet_pos.x, my.x); //Adds the first and second vector, and copies the result to first. This can be used for adding velocities
bullet_pan = my.pan; // and we use client's player pan angle for the bullet
ent_create("bullet.mdl", bullet_pos.x, move_bullets); // then create client's bullets (on the server as well)
}
if (client_fired == 1) // the client has fired? Then we need to get access to the client player
{
you = ptr_for_handle(client_ent); // let's restore its handle; the client player is now "you"
vec_set (bullet_pos.x, vector (80, -10, 25)); // we set bullet_pos to the same offset
vec_rotate (bullet_pos.x, you.pan); // in relation to the "you" model (client's player)
vec_add (bullet_pos.x, you.x);
bullet_pan = you.pan; // and we use client's player pan angle for the bullet
ent_create("bullet.mdl", bullet_pos.x, move_bullets); // then create client's bullets (on the server as well)
}
}
my.pan += my.skill2*5*time_step; // rotate the entity using its skill2
var distance = my.skill1*5*time_step;
c_move(me, vector(distance,0,0), NULL, GLIDE); // move it using its skill1
walk_percentage += distance;
ent_animate(me,"walk",walk_percentage,ANM_CYCLE); // animate the entity
wait (1);
}
}
function main()
{
if (!connection)// not started with -cl / -sv -cl?
{
sys_exit(NULL); // and then shut down the engine
}
do
{
wait(1);
}while (dplay_status < 2); // wait until the session is opened or joined
dplay_entrate = 4; // 16 ticks/4 = 4 updates per second
dplay_smooth = 0; // dead reckoning not needed
dplay_localfunction = 2;
level_load ("multiplayer6.wmb");
while (dplay_status < 6) wait(1); // wait until the level state is received from the server
on_client = on_client_event;
on_server = player_remove; // function server_event will run as soon as the server receives a message from a client
on_mouse_left = fire_bullets; // the player use the left mouse buttons to fire
if (connection == 3)// this instance of the game runs on the server
{
STRING* title = str_create("Server: ");
str_cat(title,server_ip);
video_window(0,0,0,title);
ent_create ("redguard.mdl",vector(100,50,40),player_move); // then create the red guard!
}
if(connection == 2)// runs as a connected client
{
video_window(0,0,0,player_name);
random_seed(0); // allow random player positions
my=ent_create ("blueguard.mdl",vector(-100+random(200),-50+random(100),40),player_move); // create the blue guard
client_ent = handle(my); // now we can get a valid handle to the client entity (the player that runs on the client)
send_var_to(NULL,client_ent); // and let's send the handle over the network to the server (no need to do that more than once)
}
}