purrfect ai lite-c

Posted By: darkinferno

purrfect ai lite-c - 05/22/09 15:43

@george: has the purrfect AI code from aum 64 been converted to lite-c has yet? if so, where can i find it, if not, do you plan to do a conversion? i started with 3dgs when lite-c came out, i havent played with c-script...

has anyone else converted this?
Posted By: VeT

Re: purrfect ai lite-c - 05/22/09 15:48

Yes, it works for me in lite-c, but its deep in the project, so i'd lost a lot of time, extracting it from vars and so on.
The best way for you is to rewrite it by yourself smile
Posted By: Quad

Re: purrfect ai lite-c - 05/22/09 16:04

well if you show me what exactly you want to be converted, i will try to convert.
Posted By: darkinferno

Re: purrfect ai lite-c - 05/22/09 16:11

@vet: i'd love to rewrite it myself but its pretty deep and i'd hate having to use c-script smile maybe if i was already a c-scipt user going to lite-c it wouldnt be that bad but i dont think i want to learn c-script at this point in time.

@quadraxas: dont know if this is asking too much but jus the simple demo from the aum or just in an action that i could assign to entities, give them a objective and have them go to it, i could do the rest of fleshing it out but that would be the ground work...
Posted By: Quad

Re: purrfect ai lite-c - 05/22/09 16:48

okay, i ll try to convert the aum demo... starting now.

(the pai folder from aum64 is what you need to be converted, right?)
Posted By: darkinferno

Re: purrfect ai lite-c - 05/22/09 16:55

yes, thats right and thanks for your effort
Posted By: Quad

Re: purrfect ai lite-c - 05/22/09 18:05

here it is, behaves exactly the same with c-script version:

pai.c
Code:
/////////////////////////////////////////////////////////////////////////////////////////////
// Purrfect AI is being developed by George Pirvu george@acknex.net
// 
// the maximum number of nodes that can be placed in the demo is 300
// the distance between two consecutive nodes should be smaller than 1000 quants for this demo
//
// Converted to Lite-C by Quadraxas
/////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////
#include <acknex.h>
#include <default.c>
///////////////////////////////


// maximum number of nodes that can be used in this demo
#define max_nodes 300 
// 300 x 300 (max_nodes * max_nodes)
#define max_arrays 90000 

/////////////////////////////////////////////////////////////////////////////////////////////

var number_of_nodes = -1; // stores the numbers associated to the nodes (0... up to 299 in this demo)
var current_node = 0; // the current node that scans for the other nodes
var touched_node = 0; // that's the node that is touched with the mouse pointer
var start_node = 0; 
var target_node = 0; // closest node to the player that can "see" the player

var node_to_player[max_nodes]; // distance from the node to the player
var see_player[max_nodes]; // sets the corresponding var to 1 if the node can see the player
var nodex[max_nodes]; // x coordinates for the nodes
var nodey[max_nodes]; // y coordinates for the nodes 
var node_to_node[max_arrays]; // stores the distance between any two nodes (up to 300 x 300 elements)
var visited[max_arrays]; // stores the actual path from a node to any other node (up to 300 x 300 elements)
var path[max_nodes]; // stores the elements of the actual path
var dist_to_player[max_nodes]; // holds the distance from the player to its surrounding nodes
var min_player;

var closest_distance = 999999; // will hold the distance from the closest visible node to the player
var index = 0; // used as a counter from 0 to 299
var distances_computed = 0; // will be set to 1 when the array is filled with the distances between the nodes
var next_node; // next node on the shortest path
var pathfinding_on = 1; // the player will use (or not) the path finding algorithm to go to the destination
var target_in_solid = 0; // will be set to 1 if the target entity is placed inside a solid (a level block, etc)
var nodes_visible = 0;

var i; // 4 counters with short names to make the code more readable
var j; 
var k; 
var l;

var test_var; // used for debugging

VECTOR player_pos1;
VECTOR player_pos2;
VECTOR vec_temp;

/////////////////////////////////////////////////////////////////////////////////////////////

STRING* level_wmb = "pai.wmb";
STRING* marker_mdl = "marker.mdl";
STRING* player_mdl = "player.mdl";
STRING* target_mdl = "target.mdl";
STRING* bullet_mdl = "bullet.mdl";
STRING* target_tga = "target.tga";

/////////////////////////////////////////////////////////////////////////////////////////////

BMAP* pointer_pcx = "pointer.pcx";

/////////////////////////////////////////////////////////////////////////////////////////////

SOUND* beep_sound = "beep.wav";
SOUND* door_wav = "door.wav";
SOUND* damaged_wav = "damaged.wav";
SOUND* bullet_wav = "bullet.wav";

/////////////////////////////////////////////////////////////////////////////////////////////

FONT* system_font = "system12.bmp";

/////////////////////////////////////////////////////////////////////////////////////////////

function trace_back();
function run_algorithm(); // Floyd - Dijkstra - George :) algorithm 
function follow_path();
function set_target();
function init_target();

/////////////////////////////////////////////////////////////////////////////////////////////

#define health skill40

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "spiders.c"

/////////////////////////////////////////////////////////////////////////////////////////////

ENTITY* d1_marker; // temporary pointer, guides the player along the path
ENTITY* playerd; // player's destination entity

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

TEXT* ai_txt = // displays the texts on the screen
{
	font = system_font;
	pos_x = 0;
	pos_y = 0;
	string = "Current path:   \n\nTouched:\nTarget:\nStart:";
	flags = SHOW; // remove the comment to see the debug information
}

//////////////////////////////////////////////////////////////////////////////////////////////

PANEL* ai_pan= // displays the numerical values on the screen
{
	pos_x = 0;
	pos_y = 0;
	layer = 10;
	digits = (90, 50, 4, system_font, 1, touched_node);
	digits = (90, 75, 4, system_font, 1, target_node);
	digits = (90, 100, 4, system_font, 1, start_node);

//	digits = 180, 0, 4, system_font, 1, path[0]; // display the first 15 points on the shortest path
//	digits = 220, 0, 4, system_font, 1, path[1]; // feel free to display even more if you want to
//	digits = 260, 0, 4, system_font, 1, path[2]; 
//	digits = 300, 0, 4, system_font, 1, path[3];
//	digits = 340, 0, 4, system_font, 1, path[4];
//	digits = 380, 0, 4, system_font, 1, path[5];
//	digits = 420, 0, 4, system_font, 1, path[6];
//	digits = 460, 0, 4, system_font, 1, path[7];
//	digits = 500, 0, 4, system_font, 1, path[8];
//	digits = 540, 0, 4, system_font, 1, path[9];
//	digits = 580, 0, 4, system_font, 1, path[10];
//	digits = 620, 0, 4, system_font, 1, path[11];
//	digits = 660, 0, 4, system_font, 1, path[12];
//	digits = 700, 0, 4, system_font, 1, path[13];
//	digits = 740, 0, 4, system_font, 1, path[14];

	digits(0, 0, 4, system_font, 1, player.health);

	flags = OVERLAY | SHOW; // remove the comments from the digits to see the debug information
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function set_target();
function toggle_nodes();
function fire_bullets();

function main()
{
	video_mode = 8; // set the resolution to 1024x768 pixels
	video_depth = 32; // use true color graphics
	video_screen = 1; // and start the demo in full screen mode
	
	
	on_mouse_right = set_target; // click the right mouse button to set a new target
	on_space = toggle_nodes;
	on_mouse_left = fire_bullets;
	
	vec_set(sky_color,vector(1,1,1));
	camera.arc = 80; // set the initial zoom factor
	fps_max = 100; // limit the frame rate to 100 fps
	clip_size = 0; // show all the triangles for all the models
	mouse_range = 50000; // allow the mouse pointer to click any entity up to 50,000 quants away
	level_load(level_wmb);
	wait (3); // wait for the level to be loaded
	while (distances_computed == 0) {wait (1);} // wait until the big array is filled with the distances between the nodes
	run_algorithm(); // get the shortest paths between the nodes
	media_loop ("track1.mp3", NULL, 60);
	while (1)	
	{
		index %= (number_of_nodes + 1); // limit "index" to 0... up to 299 in this demo
		if ((node_to_player[index] < closest_distance) && (see_player[index] == 1))
		// if the distance from this node to the player is smaller than the current distance to the player
		// and we are dealing with a node that can see the player
		{
			closest_distance = node_to_player[index]; // then we have a new winner!
			target_node = index; // and the target is given by the current "index" value
		}
		else // haven't found a new winner?
		{
			closest_distance = node_to_player[target_node]; // then use the previous winner
		}
		index += 1; // move on to the following node
		wait (1);
	}		
}

function init_mouse_startup()//functions ending in _startup is auto-started.
{
	mouse_map = pointer_pcx; // set the mouse pointer bitmap
	mouse_mode = 2; // and then make it visible
	while (1)
	{
		mouse_pos.x = mouse_cursor.x; // allow the mouse to move
		mouse_pos.y = mouse_cursor.y; // on the x and y axis
		wait (1);
	}
}

function set_camera_startup()
{
	while (player == NULL) {wait (1);} // wait until the player entity is loaded
	camera.pan += 45;
	camera.z = player.z + 500; // place the camera 500 quants above player's head at game start
	camera.tilt = -60; // make it look downwards
	while (1)
	{
		camera.x = player.x - camera.z / 2; // place the camera behind the player on the x axis
		camera.y = player.y - camera.z / 2;
		camera.z += 0.3 * mickey.z; // and allow the player to zoom in / out using the mouse wheel
		camera.z += 15 *  (key_q - key_a) * time_step; // or the "+" and "-" keys
		camera.z = clamp (camera.z, 250, 2000); // limit the height of the camera to 250...2000 quants
		wait (1);
	}
}

function run_algorithm()
{
	i = 0;
	j = 0;
	while (i < (number_of_nodes + 1))
	{
		while (j < (number_of_nodes + 1))
		{
			index = j + i * (number_of_nodes + 1); // compute the correct index in the array
			if (node_to_node[index] == 0) // if these nodes can't see each other
			{
				node_to_node[index] = 999999; // set a huge distance for the nodes that can't see each other
			}
			j += 1;
		}
		i += 1;
		j = 0;
	}
	i = 0;
	j = 0;
	k = 0;
	while (i < (number_of_nodes + 1))
	{
		while (j < (number_of_nodes + 1))
		{
			while (k < (number_of_nodes + 1)) // go through all the nodes
			{
				// if dist(2,10) + dist(10,15) < dist(2,15) then 10 is a point on a shorter path 
				if ( (node_to_node[j + i * (number_of_nodes + 1)] + node_to_node[i + k * (number_of_nodes + 1)]) < (node_to_node[j + k * (number_of_nodes + 1)]))
				{
					// make the change: don't use the old (2,15) (2, 10, 15 are used just as an example here)
					node_to_node[j + k * (number_of_nodes + 1)] = node_to_node[j + i * (number_of_nodes + 1)] + node_to_node[i + k * (number_of_nodes + 1)];
					visited[j + k * (number_of_nodes + 1)] = i; // store the visited point
				}
				if (j == k) // dist(1,1) = dist(2,2) = dist(3,3).... = dist(29,29) should be set to 0 (not necessary but looks nicer)
				{
					node_to_node[j + k * (number_of_nodes + 1)] = 0; // otherwise we would get d(1,1) = d(1,2) + d(2,1) if "2" is the closest node to "1"
					visited[j + k * (number_of_nodes + 1)] = 0; // same thing here; the distance between a visited node and itself should be zero
				}
				k += 1;
			}
			k = 0;
			j += 1;
		}
		k = 0;
		j = 0;
		i += 1;
	}
}

function find_path(l, j)
{
	i = 0;
	while (i < (number_of_nodes + 1)) // search from this node for the rest of the nodes (up to 299 more nodes)
	{
		if ((i != j) && (visited[i + (number_of_nodes + 1) * j] == 0)) // the current node can "see" these nodes, excepting itself of course (i != j)
		{
			if (node_to_node[l + (number_of_nodes + 1) * j] == node_to_node[l + (number_of_nodes + 1) * i] + node_to_node[i + (number_of_nodes + 1) * j]) 
			// that's a node on the shortest path because the shortest path includes it
			{
				k += 1; // move to the next array element
				path[k] = i; // and store the node number inside the array
				next_node = i; // store the new node on the shortest path because it will loose its value right away
				i = (number_of_nodes + 1) - 1; // don't test other values - eliminate other paths that have the same length (that could happen in some levels)
				if (path[k] == l) // end of search, reached the starting point (l = start_node, remember?)
				{
					return; // get out of this function
				}
			}
		}
		i += 1; // increase i if the function continues to run
	}
	j = next_node; // set the next node on the path as target
	find_path(l, j); // recursive function, search for the shortest path again (if it searched (2,10) now searches (2,9) and so on)
}

action node() // use up to 300 nodes in this demo
{
	set(my,INVISIBLE | TRANSLUCENT | PASSABLE);
	my.alpha = 25;
	my.emask |= ENABLE_SCAN | ENABLE_TOUCH | ENABLE_CLICK;
	my.event = trace_back;
	my.skill47 = 1234; // set this weird value for skill47 = I'm a node
	number_of_nodes += 1; // get a unique id number (0.. up to 299)
	my.skill48 = number_of_nodes; // and store it in skill48
	nodex[my.skill48] = my.x; // store x and y
	nodey[my.skill48] = my.y; // for this node
	wait (-1); // wait until all the nodes are placed in the level
	while (current_node <= number_of_nodes)
	{
		if (current_node == my.skill48) // I'm the current node!
		{
			c_scan(my.x, my.pan, vector(360, 30, 1000), IGNORE_ME);
			wait (1); // not needed but won't hurt anybody
			current_node += 1; // move to the next node
		}
		wait (1);
	}
	distances_computed = 1;
	while (playerd == NULL) {wait (1);} // wait until the player destination entity is created
	while (1)
	{
		node_to_player[my.skill48] = vec_dist(playerd.x, my.x); // store the distance to the player destination entity
		if (node_to_player[my.skill48] < 500) // if the player destination is closer than 500 quants to this node
		{
			// if this node can "see" the player destination entity
			if (c_trace (my.x, playerd.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) 
			{
				see_player[my.skill48] = 1; // the node can see the player destination
			}
			else
			{
				see_player[my.skill48] = 0; // this node can't see the player destination
			}
		}
		wait (1 + random(10)); // spread the cpu load over 10 consecutive frames
		if (nodes_visible == 0)
		{
			set(my,INVISIBLE);
		}
		else
		{
			reset(my,INVISIBLE);
		}
	}
}

function init_target()
{
	playerd = my; // I'm the player destination (sets the initial target)
	set(my,INVISIBLE | PASSABLE);
}

action players_action()
{
	//init vectors -set all components to 0-
	vec_zero(vec_temp);
	vec_zero(player_pos1);
	vec_zero(player_pos2);
	player = my;
	ent_create (target_mdl, my.x, init_target);
	my.skill47 = 5678; // I'm the player
	my.health = 100; // and I've got 100 health points
	var previous_target;
	while (my.health > 0)
	{
		if (vec_dist (playerd.x, my.x) <= 30)
		{
			ent_animate(me, "stand",my.skill46, ANM_CYCLE);
			my.skill46 += 2 * time_step; // "stand" animation speed
			wait (1);
		}
		else
		{
			if (pathfinding_on == 1) // need to use the path finding algorithm?
			{
				min_player = 999999; // set the minimum distance from the player destination to its surrounding nodes to a huge value
				c_scan(my.x, my.pan, vector(360, 360, 800), IGNORE_ME);
				wait (-0.2);
				k = 0; 
				while (k < (number_of_nodes + 1))
				{
					path[k] = 0; // reset the array, get rid of the previously stored path
					k += 1;
				}
				k = 0; // start with the first element of path[] array
				path[k] = target_node; // write the first path element
				previous_target = target_node;
				if (start_node != target_node)
				{
					find_path(start_node, target_node); // find the shortest path between these two nodes
					wait (2); // wait for the path to be computed
					i = k; // k = number of nodes on the shortest path
					vec_temp.x = nodex[path[i-1]]; // get the previously stored coordinates for this node
					vec_temp.y = nodey[path[i-1]]; // on x and y 
					vec_temp.z = my.z; // use player's height for z (you should use a trace to place the z on the ground if your level isn't flat)
					d1_marker = ent_create (marker_mdl, vec_temp, follow_path); // create an invisible entity that guides the player
					wait (1);
					// the player can see the second node on the path directly?
					if(c_trace (player.x, d1_marker.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) 
					{
						i -= 1; // then ignore the first point of the path (don't turn back to it)
					}
					while (i >= 0) // decrease i until it goes below zero
					{
						vec_temp.x = nodex[path[i]]; // get the previously stored coordinates for this node
						vec_temp.y = nodey[path[i]]; // on x and y 
						vec_temp.z = my.z; // use player's height for z (you should use a trace to place the z on the ground if your level isn't flat)
						d1_marker = ent_create (marker_mdl, vec_temp, follow_path); // create an invisible entity that guides the player
						wait (1);
						while (vec_dist (d1_marker.x, my.x) > 30)
						{
							ent_animate(me, "walk", my.skill46, ANM_CYCLE);
							my.skill46 += 8 * time_step; // "walk" animation speed
							my.skill1 = 6 * time_step;
							my.skill2 = 0;
							my.skill3 = 0;
							result = c_move (my, my.skill1, nullvector, IGNORE_PASSABLE | GLIDE);
							// got stuck or the player has decided to pick a new path while the player was already moving or close to the destination?
							if ( (result == 0) || (previous_target != target_node) || (vec_dist (playerd.x, my.x) < 100) ) 
							{
								break; // get out of both while loops, find the path again
							}
							vec_set (player_pos1.x, my.x);
							wait (1);
							vec_set (player_pos2.x, my.x);
						}
						// the target is visible from here?
						if(c_trace (player.x, playerd.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) 
						{
							pathfinding_on = 0; // then bypass the path finding algorithm
							i = -1; // and get out of the "while" loop to stop the path finding
						}
						i -= 1; // move on to the following node
						wait (1);
					}
					// go from the last node that can see the destination (the "playerd" entity) to the actual destination entity
					while ((vec_dist (playerd.x, my.x) > 30) && (mouse_right == 0) && (previous_target == target_node))
					{
						ent_animate(me, "walk", my.skill46, ANM_CYCLE);
						my.skill46 += 8 * time_step; // "walk" animation speed
						vec_set (vec_temp, playerd.x); 
						vec_sub (vec_temp, my.x);
						vec_to_angle (my.pan, vec_temp);
						my.tilt = 0;
						my.skill1 = 6 * time_step;
						my.skill2 = 0;
						my.skill3 = 0;
						result = c_move (my, my.skill1, nullvector, IGNORE_PASSABLE | GLIDE);
						if (result < (0.5 * my.skill1 * time_step)) // had to slow down or stop?
						{
							break; // then get out of here and compute a new path
						}
						vec_set (player_pos1, my.x);
						wait (1);
						vec_set (player_pos2, my.x);
					}
				}
			}
			else // the target is visible directly, so the player can move there without using the path finding algorithm
			{
				while ((vec_dist (playerd.x, my.x) > 30) && (mouse_right == 0))
				{
					ent_animate(me, "walk", my.skill46, ANM_CYCLE);
					my.skill46 += 8 * time_step; // "walk" animation speed
					vec_set (vec_temp, playerd.x); 
					vec_sub (vec_temp, my.x);
					vec_to_angle (my.pan, vec_temp);
					my.tilt = 0;
					my.skill1 = 8 * time_step;
					my.skill2 = 0;
					my.skill3 = 0;
					result = c_move (my, my.skill1, nullvector, IGNORE_PASSABLE | GLIDE);
					if (result < (0.5 * my.skill1 * time_step)) // had to slow down or stop?
					{
						break; // then get out of here and compute a new path
					}
					vec_set (player_pos1, my.x);
					wait (1);
					vec_set (player_pos2, my.x);
				}
				pathfinding_on = 1; // the player can now use the path finding algorithm again
			}
		}	
	}
	snd_play (damaged_wav, 80, 0);
	while (my.tilt < 90)
	{
		my.tilt += 10 * time_step;
		my.z -= 3 * time_step;
		wait (1);
	}
	wait (-2); // guess why I have used this line of code ;)
}

function trace_back()
{
	if (event_type == EVENT_SCAN)
	{
		if (you.skill47 == 1234) // scanned by a node
		{
			my.skill45 = handle(you); // store "you" because trace will destroy it
			// if this node can "see" the node that scanned it
			if (c_trace (my.x, you.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) 
			{
				you = ptr_for_handle(my.skill45); // restore the "you" pointer
				index = you.skill48 + my.skill48 * (number_of_nodes + 1);
				node_to_node[index] = vec_dist(my.x, you.x);
			}
		}	
		else // scanned by the player (skill47 = 5678)
		{
			my.skill45 = handle(you); // store "you" because trace will destroy it
			// if this node can "see" the player that scanned it
			if (c_trace (my.x, you.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) 
			{
				you = ptr_for_handle(my.skill45); // restore the "you" pointer
				dist_to_player[my.skill48] = vec_dist (my.x, you.x);
				if ((dist_to_player[my.skill48] < min_player) && (dist_to_player[my.skill48] != 0))
				{
					min_player = dist_to_player[my.skill48];
				}
				wait (2); // allow the rest of the nodes to change min_player (if they should change it)
				if (dist_to_player[my.skill48] == min_player) // this is the closest node to the player?
				{
					start_node = my.skill48;
				}
			}
			else
			{
				dist_to_player[my.skill48] = 0;
			}				
		}
	}
	if (event_type == EVENT_TOUCH)
	{
		touched_node = my.skill48; // get the number of the node that was touched with the mouse pointer
	}
}

function follow_path()
{
	set(my,INVISIBLE | PASSABLE);
	vec_set (vec_temp.x, my.x); 
	vec_sub (vec_temp.x, you.x);
	vec_to_angle (you.pan, vec_temp); // turn the player towards the path marker
	while (d1_marker == my) {wait (1);} 
	ent_remove (my); // remove the old, unused markers
}

function set_target()
{
	while (mouse_right == 1) {wait (1);} // wait for the mouse button to be released
	pathfinding_on = 0; // disable the path finding algorithm temporarily
	VECTOR pos1;
	vec_zero(pos1);
	VECTOR pos2;
	vec_zero(pos2);
	pos1.x = mouse_pos.x;
	pos1.y = mouse_pos.y;
	pos1.z = 0;
	vec_for_screen (pos1, camera);
	pos2.x = mouse_pos.x;
	pos2.y = mouse_pos.y;
	pos2.z = 50000; // use a big value here
	vec_for_screen (pos2, camera);
	c_trace (pos1.x, pos2.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX);
	vec_set (playerd.x, target.x);
	playerd.z = player.z; // set the height of the target ("playerd") to player's z coordinate
	if (c_content (playerd.x,0) == 3)//content_solid
	{
		target_in_solid = 1; // for further use
		vec_set (playerd.x, player.x); // don't move the player anymore (move the destination entity at its position)
	}
	if(c_trace (player.x, playerd.x, IGNORE_ME | IGNORE_MODELS | IGNORE_PASSENTS | USE_BOX) == 0) // the player can see the destination directly?
	{
		pathfinding_on = 0; // then bypass the path finding algorithm
	}
	else
	{
		pathfinding_on = 1; // use the path finding algorithm
	}
}

function toggle_nodes()
{
	nodes_visible += 1;
	nodes_visible %= 2;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// pure game code (not related to the AI code) from here on
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ENTITY* bullet_target;

VECTOR bullet_origin;

action passable_door() // these particular doors need to be passable because the nodes need to see each other through them
{
	while (player == NULL) {wait (1);}
	set(my,PASSABLE);
	while (vec_dist (player.x, my.x) > 100) {wait (1);}
	my.skill10 = my.y;
	snd_play (door_wav, 100, 0);
	while (my.y < my.skill10 + 120)
	{
		my.y += 10 * time_step;
		wait (1);
	}
}

function remove_bullets() // this function runs when the bullet collides with something
{
	wait (1); // wait for a frame
	ent_remove (my); // and then remove the bullet
}

function move_bullets()
{
	VECTOR temp_angle;
	vec_zero(temp_angle);
	VECTOR bullet_speed; // this vec will store the speed of the bullet
	vec_zero(bullet_speed);
	my.skill99 = 135; // uniquely identify a bullet
	
	my.emask |= ENABLE_IMPACT | ENABLE_ENTITY | ENABLE_BLOCK;
	set(my,PASSABLE);
	vec_set(temp_angle, bullet_target.x); 
	vec_sub(temp_angle, my.x); 
	vec_to_angle(my.pan, temp_angle);
	if (vec_dist (player_pos1.x, player_pos2.x) == 0) // the player is standing still?
	{
		player.pan = my.pan; // then rotate the player in the proper direction as well
	}
	my.event = remove_bullets; // when it collides with something, its event function (remove_bullets) will run
	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; // then don't allow the gravity to have its ways with the bullet
	while (my)
	{
		if (vec_dist (my.x, bullet_origin.x) > 50) {reset(my,PASSABLE);}
		c_move (my, bullet_speed, nullvector, IGNORE_PASSABLE);
		wait (1);
	}
}

function show_target()
{
	set(my,BRIGHT);
	vec_to_angle (my.pan, normal); // orient the sprite correctly
	vec_add(my.x, normal.x); // move the sprite a bit away from the wall
	set(my,PASSABLE);
	my.scale_x = 0.2;
	my.scale_y = my.scale_x;
	while (mouse_left == 1) {wait (1);} // wait until the player releases the mouse button
	ent_remove (me); // remove the old target
}

function set_bullet_target()
{
	VECTOR pos1;
	VECTOR pos2;
	vec_zero(pos1);
	vec_zero(pos2);
	
	pos1.x = mouse_pos.x;
	pos1.y = mouse_pos.y;
	pos1.z = 0;
	vec_for_screen (pos1, camera);
	pos2.x = mouse_pos.x;
	pos2.y = mouse_pos.y;
	pos2.z = 200000; // use a big value here
	vec_for_screen (pos2, camera);
	c_trace (pos1, pos2, IGNORE_ME | IGNORE_PASSABLE | IGNORE_MODELS | IGNORE_SPRITES); 
	bullet_target = ent_create (target_tga, target, show_target);
}

function fire_bullets()
{
	if (player.health <= 0) {return;} // don't fire bullets if the player is dead
	proc_kill(4); // don't allow more than 1 copy of this function to run
	set_bullet_target();
	while (bullet_target == NULL) {wait (1);} // wait until the target sprite is displayed on the floor
	while (mouse_left == 1) // this loop runs for as long as the left mouse button is pressed
	{
		vec_for_vertex (bullet_origin.x, player, 17); // get the coordinates for the bullets' origin
		 // create the bullet and attach it the "move_bullets" function
		ent_create (bullet_mdl, bullet_origin.x, move_bullets);
		snd_play (bullet_wav, 100, 0); // play the bullet sound at a volume of 100
		wait (-0.14); // fire 7 bullets per second (0.14 * 7 = 1 second)
	}
}



spiders.c
Code:
///////////////////////////////////////////////////////////////////////////////////////////////////////
// copy the files inside this folder in your game folder
// don't forget to include spiders.wdl in your main game file
// place a model in your level and attach it the deadly_spider action
///////////////////////////////////////////////////////////////////////////////////////////////////////

SOUND* gotcha_wav = "gotcha.wav";
SOUND* dead_wav = "dead.wav";

///////////////////////////////////////////////////////////////////////////////////////////////////////

STRING* spider_mdl = "spider.mdl";

///////////////////////////////////////////////////////////////////////////////////////////////////////

function hurt_them();
function deadly_spider();

///////////////////////////////////////////////////////////////////////////////////////////////////////

function init_spider()
{
	VECTOR movement_speed;
	vec_zero(movement_speed);
	my.pan = random(360);
	movement_speed.x = (20 - random(40));
	movement_speed.y = (20 - random(40));
	while (my.z < 150)
	{
		my.x += movement_speed.x * time_step;
		my.y += movement_speed.y * time_step;
		my.z += 20 * time_step;
		wait (1);
	}
	while (my.z > 45)
	{
		my.x += movement_speed.x * time_step;
		my.y += movement_speed.y * time_step;
		my.z -= 10 * time_step;
		wait (1);
	}
	deadly_spider();
}

function generate_spiders_startup()
{
	while (player == NULL) {wait (1);}
	while (player.x < -100) {wait (1);} // wait until the player enters the big, empty area
	var i = 0;
	while (i < 5)
	{
		snd_play (gotcha_wav, 100, 0);
		ent_create (spider_mdl, vector (-362, 1335, 45), init_spider);
		ent_create (spider_mdl, vector (-286, 762, 45), init_spider);
		ent_create (spider_mdl, vector (82, 1195, 45), init_spider);
		ent_create (spider_mdl, vector (254, 826, 45), init_spider);
		ent_create (spider_mdl, vector (295, 1506, 45), init_spider);
		i += 1;
		wait (-0.5);
	}
}

function deadly_spider()
{
	VECTOR spider_speed;
	vec_zero(spider_speed);
	VECTOR in_front;
	vec_zero(in_front);
	
	my.emask |= ENABLE_ENTITY | ENABLE_IMPACT | ENABLE_BLOCK;
	my.event = hurt_them;
	my.health = 1; // "health" was defined as skill40 inside pai.wdl
	while(my.health > 0)
	{
		spider_speed.x = 8 * time_step; 
		spider_speed.y = 0;
		spider_speed.z = 0;
	//	spider_speed *= time_step;
		in_front.x = my.x + 40 * cos(my.pan);
		in_front.y = my.y + 40 * sin(my.pan);
		in_front.z = my.z;
		if (c_content(in_front,0) == 3)//content_solid
		{
			my.skill40 = my.pan;
			my.skill41 = 30 + random(90);
			while (my.pan < my.skill40 + my.skill41) // rotate 30..120 degrees
			{
				my.pan += 5 * time_step;
				wait (1);
			}
		}
		else // free to move
		{
			ent_animate(me, "run", my.skill20, ANM_CYCLE);
			my.skill20 += 10 * time_step; 
			result = c_move (me, spider_speed, nullvector,IGNORE_PASSABLE);
			if (result == 0) // got stuck?
			{
				spider_speed.x *= -1; // then reverse the movement direction
				my.skill40 = my.pan;
				my.skill41 = 30 + random(90);
				while (my.pan < my.skill40 + my.skill41) // rotate 30..120 degrees
				{
					my.pan += 5 * time_step;
					ent_animate(me, "run", my.skill20, ANM_CYCLE);
					my.skill20 -= 10 * time_step; 
					c_move (me, spider_speed, nullvector,IGNORE_PASSABLE);
					wait (1);
				}
				spider_speed.x *= -1; // restore the initial speed
			}
			if ((vec_dist (player.x, my.x) < 30) && (player.health > 0)) // collided with the player?
			{
				snd_play (gotcha_wav, 50, 0);
				player.health -= 1 * time_step; // subtract 10 health points from the entity's health
			}
		}
		wait (1);
	}
	my.skill20 = 0;
	set(my,PASSABLE);
	snd_play (dead_wav, 90, 0);
	while (my.skill20 < 95)
	{
		if (my.roll < 180) {my.roll += 25 * time_step;}
		if (my.z > 30) {my.z -= 5 * time_step;}
		ent_animate(me, "death", my.skill20, ANM_CYCLE);
		my.skill20 += 10 * time_step; 
		wait (1);
	}
	wait(-1);
}

function hurt_them()
{
	proc_kill(4);
	if (you != NULL) // collided with an entity (the player, a monster, etc)?
	{
		if (you.skill99 == 135) // hit by player's bullet?
		{
			my.health = 0; // then this spider is dead
			my.event = NULL; // so it doesn't react to events anymore
		}
	}
}


Posted By: darkinferno

Re: purrfect ai lite-c - 05/22/09 18:12

thank you! you're a god sent... i can finally move on... i'll get it implemented and post its progress soon smile
Posted By: George

Re: purrfect ai lite-c - 05/23/09 04:50

Thank you, that's a great effort and will probably get me started in adding more features to the AI code in the near future.
Posted By: darkinferno

Re: purrfect ai lite-c - 05/23/09 12:04

i got it working, however, how accurate is this pathfinding? in some scenarios, the player tries to walk through walls, i take it because the nodes are reading each other incorrectly, i'm working at fixing this, this error only happens when i zoom out and chose a node thats at the other extreme end...
Posted By: darkinferno

Re: purrfect ai lite-c - 05/23/09 13:08

oh my, i apologize laugh .. turns out the door entities interfere with the pathfinding... the pathfinding in itself is perfect but doesnt it handle realtime changes to the level ...
Posted By: Quad

Re: purrfect ai lite-c - 05/23/09 13:22

yes, i also noticed that, but this is same in c-script version.
(though you can check aum27-aum something, for explanation of the system, may help)

edit: aha, thanks for the info laugh
Posted By: darkinferno

Re: purrfect ai lite-c - 05/23/09 13:32

so quadraxas? how would you go about adding obstacle avoidance, since my levels are model based and i cant use c_content, i dont expect you to code it, just looking for some ideas, even though i do have a few in mind...
Posted By: Quad

Re: purrfect ai lite-c - 05/23/09 14:07

first thing comes to mind is to use c_trace from player/entity to place you want to check, if it hits sometings you can't go there.(thinking of checking if front of entity is blocked or not)
Posted By: darkinferno

Re: purrfect ai lite-c - 05/23/09 14:54

ok, this is rather pathetic, but we all have to start somewhere.. i need to learn a few basics, afterall, this is the first time i'm gonna gonna try at making somewhat of a detailed AI, i just need a few pointers, i knew these problems would arise though... help in the following are would be nice:

- how do i set different targets for each AI, currently it uses a global entity so all ents try to go towards it, i figure i can use a entity for each AI but is this the best method?

wait, thats basically it laugh... it'd be cool if some1 could help me through this... if they have the time that is...
Posted By: Quad

Re: purrfect ai lite-c - 05/23/09 15:55

*turns face to George* - George?


maybe it's time for new ai series
Posted By: darkinferno

Re: purrfect ai lite-c - 05/23/09 16:09

lol... lol... maybe laugh... he'll probably just update perfectAI though... its not that i cant use this, i just want some pointers and some common AI ideas...
Posted By: George

Re: purrfect ai lite-c - 05/23/09 22:04

One of my perfect AI demos has realtime obstacle detection (can't remember the magazine number, though). The following versions use precomputed paths, but from what I remember only one of the functions needs to be changed. I might improve the code in the near future and post it in the AUM.
Posted By: darkinferno

Re: purrfect ai lite-c - 05/30/09 11:13

ok... i was working with intense for a while, right now i'm just trying to make a decision on which one to use but for the perfect AI, how would i have to modify the code so i could add more ai, currently its setup to be used for the player ent... when i add new ai they all try to go to the same target...
Posted By: the_clown

Re: purrfect ai lite-c - 05/30/09 13:10



If you mean team AI, here's an example made by badapple.

http://www.freewebs.com/baddapple/freelitecscript.htm

The example shows how to code two teams. In the example, they use swords, but it's not to hard to get the concept behind that.
Posted By: darkinferno

Re: purrfect ai lite-c - 05/30/09 13:30

no, i have that demo, what i mean was... perfect A is setup to use ONE target model and ONE character...

the target model is globally defined so that means if i wanted to add another AI, they would both go to the same target, by making the target local, i get errorss because the NODES also uses the globally defined target pointer...

i wanted to have an idea of the changed i would have to make to perfect AI so i could use more AI with their OWN individual targets...
Posted By: George

Re: purrfect ai lite-c - 06/01/09 06:34

Set a particular skill or a flag for one of the teams. Let's say that the red team has its flag1 set to on, while the blue team doesn't have it set. Then, make them all scan around; if a model belongs to the red team and detects an entity with its flag1 set to on, it shouldn't attack it (it's from the same red team); otherwise, if its flag1 isn't on, it is from the blue team so it should be attacked. That's how any team-based AI code works.
Posted By: darkinferno

Re: purrfect ai lite-c - 06/12/09 19:29

true, that should work but all the ai go to one global pointer, i cant make it local because even the nodes need to use that pointer so i cant give each ai their own targets unless i ofcourse create 8 AI and say 8 target models
Posted By: George

Re: purrfect ai lite-c - 06/13/09 04:37

The enemies can have their own targets; simply make them scan around, detecting the status of that flag for all the entities that are near them.
© 2024 lite-C Forums