Sure,
This is a relatively simple process. Really a three step process, click, engage spell, spell movement. But they are connected to much larger things, depending on the complexity of your game.
To target a mob, you first must know a number of things:
-What part of the code recognizes that the mob you want to target is really there. (ergo. it must be a unique entity, unless you are only planning on having 1 mob at a time.)
-What method you will use to say that the mob is targeted and therefor you can cast a spell at it. I assume that if your game has targeting spells (such as WOW), then you cannot cast a spell on the target without it being targeted.
- How will you know, as the player, that the mob is targeted.
- How will the enemies HUD bars be displayed, and will they show up when you target it, and then dissapear after you deselect it, or the enemie dies.
I'll use a single enemy type as an example. Let's say I have a group of enemies all patrollling around a mob spawn area. They are all flying enemies called "sensored", heh (wont give out my names yet). Although they are the same entity, they al have a unique id, and you can select each one sperately, just like in bigtime games (I believe I mentioned WOW, which is similar to my game).
Once you know these things you can do something like this:
Code:
function enemy_selection()
{
if ((event_type == EVENT_CLICK) && (my.LIFE > 0))
{
if (player.ENEMY != 0)
{
player.ENEMY = 0;
wait (1);
reset (hud_bar_mntqlx, VISIBLE);
reset (hud_portrait_mntqlx, VISIBLE);
reset (hud_backing_mntqlx, VISIBLE);
ent_remove(selection_marker);
}
wait (1);
player.ENEMY = handle(my);
if (marker_toggle == 0) selection_marker = ent_create ("marker.tga", vector (my.x, my.y, my.z+30), marker_orientation);
set (hud_bar_mntqlx, VISIBLE);
set (hud_portrait_mntqlx, VISIBLE);
set (hud_backing_mntqlx, VISIBLE);
}
// if you'd like to be able to toggle through enemies starting with the closest one, using tab, uncomment this
// if ((event_type == EVENT_SCAN) && (key_tab == 1))
// {
// if (player.ENEMY != 0)
// {
// player.ENEMY = 0;
// wait (1);
// ent_remove(selection_marker);
// }
// wait (1);
// player.ENEMY = handle(my);
// if (marker_toggle == 0) selection_marker = ent_create ("marker.tga", vector (my.x, my.y, my.z+30), marker_orientation);
// }
}
This function can be used as a kind of universal function by replacing a few things. As of now it will ony work for the enemy type, bird. It basicly states "If I've clicked on yiou,and I'm alive, and your an enemy, make you my enemy." Now I have a target, and cannot target anything else. Then the code will make the HUD visible for that enemy, and remove any other targeting markers that are out there. For instance I had another enemy targeted, the cursor that surrounds the enemy would be removed. I then create another marker on that enemy, and set its handle up. With a few more bits of code, I am then ready for enemy selection on my current target.
This next function simply handles the markers orientation on the enemy, and it will be changed to match your preference on how you want it set up. But the function is simple. While the enemy is alive, if it is targeted, you will need to decide how you want the marker oriented on the enemy. I have mine set at the enemies feet at all times. There are two lines that you willl not recognize yet. But I wil lget to them later. They will be used for two sperate effects, one of them is melee, and the other a spell. They are simply vectors, that keep track of the enemies position at all times. Why? Simply because we need to know where to create special effects when we cast spells, or use a skill. And we only really want that to happen on the enemy we have targeted (unless its a AOE spell, but thats for another day).
So here is that function:
Code:
function marker_orientation()
{
while (you.LIFE > 0)
{
vec_set (chargebolt_vector02.x, you.x);
vec_set (weapon_impact01_vec.x, you.x);
set (my, PASSABLE);
my.alpha = you.alpha;
my.scale_x = .4;
my.scale_y = .4;
my.tilt = 90;
vec_set (my.x, you.x);
wait (1);
}
ent_remove (my);
}
Now that is basicly all of the code we need for targeting, lets move onto the next step, the enemy its self. We have to know how its created, and how it will act.
This is really 2 simple functions and a action that you assign to the spawn point, once you have placed it in your level. You will of course need to adjust all three of these functions to suit your needs.
The first function simply makes the enemy that is being spawned invisible, untill it is ready to be placed in the world.
Code:
function enemy_spawn_countdown() {wait (-2); reset (my, INVISIBLE);}
The second function simple spawns enemies untill the desired number is reached. If any enemeis are dead, or there are less than the amount needed, it wil always respawn more enemies (10 in this case). You will need to decide in WED, all of the possible spawn points for that particular spawn location, and asign them to the variables used inthe function. Because the function runs through those variables and randomly places the enemies based on those values. I believe goerge has created code similar to this in one of the magazines. I based my function off of his.
Code:
function mountain_qualix_sp01 ()
{
while (1)
{
while (mountain_qualix_sp01_max <= 10)
{
wait (-10);
mountain_qualix_index = 3 * integer(random(5));
mountain_qualix_spawnpoint01_vector.x = mountain_qualix_spawnpoint01_positioning[mountain_qualix_index + 0];
mountain_qualix_spawnpoint01_vector.y = mountain_qualix_spawnpoint01_positioning[mountain_qualix_index + 1];
mountain_qualix_spawnpoint01_vector.z = mountain_qualix_spawnpoint01_positioning[mountain_qualix_index + 2];
mountain_qualix = ent_create ("Moujtain_Qualix.mdl", mountain_qualix_spawnpoint01_vector, enemy_init_mountain_qualix);
if (mountain_qualix_index > 4) mountain_qualix_index = 0;
mountain_qualix_sp01_max += 1;
wait (1);
}
wait (1);
}
}
This last action is simple the handler for the spawn point, that you set up in wed:
Code:
action spawn_point01 ()
{
set (my, INVISIBLE);
while (1)
{
spawn_point01_locater = me;
spawn_point01_speed.z = 8 * time_step;
vec_set (spawn_point01_vector.x, my.x);
spawn_point01_vector.z -= 5000;
distance_to_ground = c_trace (my.x, spawn_point01_vector.x, IGNORE_ME | USE_BOX | IGNORE_PASSABLE | IGNORE_FLAG2);
spawn_point01_speed.z = - (distance_to_ground - 17); // 17 = experimental value
spawn_point01_speed.z = maxv (-35 * time_step, spawn_point01_speed.z); // 35 = falling speed
c_move (my, spawn_point01_speed, nullvector, IGNORE_MODELS | GLIDE | IGNORE_FLAG2);
wait (1);
}
}
Now we have enemies that spawn, and a way to target them, we only need to realize two more things (at least in this example, heh)
-Spells
-melee or ranged (melee in this case)
Starting with spells, we have the ever atypical "Chargebolt". Mine has two sperate particle effects, as well as a special shell that is created around the player, and the mob when its needed. It has a two second casting time (I wont get into timers for casting). So the shell is created while the casting time counts down, the spell is then luanched, and hits the enemy. After the enemy is hit, the spell dissapears, but the "after shock" effect takes place in its wake, and will pefectly match the neemies location, regardless of movement.
I first create the spell, and set it at the players hands of course:
Code:
function chargebolt_create()
{
casting_spell = 1;
vec_for_vertex (chargebolt_vector, player, 250);
spell_chargebolt = ent_create ("test_spell.mdl", chargebolt_vector, chargebolt_move);
spell_chargebolt_shell01 = ent_create ("chargebolt_outershell.mdl", chargebolt_vector, chargebolt_shell_move);
}
I then move the spell, and its nice little particle trail:
Code:
function chargebolt_move()
{
set (my, PASSABLE);
set (my, INVISIBLE);
wait (-2);
casting_spell = 0;
if (player.ENEMY != NULL)
{
while ((player.ENEMY != NULL) && (vec_dist (my.x, chargebolt_vector02.x) > 50))
{
chargebolt_particles = 1;
effect(chargebolt_particlesinit,maxv(1,chargebolt_particles*time_step),my.x,nullvector);
effect (chargebolt_trail, 10, my.x, nullvector);
vec_set (temp_spell01, chargebolt_vector02.x);
vec_sub (temp_spell01, my.x);
vec_to_angle (my.pan, temp_spell01);
my.tilt = 0;
my.lightrange = random( 1000 ) - 500;
set (my, SPOTLIGHT);
vec_set(d3d_spotlightcone,vector(90,90,-5)); // 90 degrees light cone, no darker area, no falloff
my.blue = 255;
c_move (me, vector (30 * time_step,0,0), nullvector, NULL);
wait (1);
}
ent_remove (my);
ent_remove (spell_chargebolt_shell01);
spell_chargebolt_shell02 = ent_create ("chargebolt_aftereffect.mdl", chargebolt_vector02.x, chargebolt_aftereffect_fading01);
}
}
This will move the second effect, or "shell" of the spell:
function chargebolt_shell_move()
Code:
function chargebolt_shell_move()
{
set (my, PASSABLE);
set (my,BRIGHT);
while (spell_chargebolt != NULL)
{
my.scale_x = random (.3) - .1;
my.scale_y = random (.3) - .1;
my.scale_z = .4;
my.lightrange = random( 1000 ) - 500;
my.blue = random (255) - 150;
vec_set (my.x, spell_chargebolt.x);
my.alpha = random( 100 ) - 50;
my.pan = random( 360 ) - 0;
my.tilt = random( 90 ) - 0;
my.roll = random( 360 ) - 0;
wait (1);
}
my.lightrange = 0;
}
This will remove the after shock effect aroudn the enemy:
Code:
function chargebolt_aftereffect_fading01()
{
set (my,BRIGHT);
set (my, PASSABLE);
while (my.alpha > 0)
{
my.lightrange = random( 1000 ) - 500;
vec_set (my.x, chargebolt_vector02.x);
vec_set (my.z, chargebolt_vector02.z + 100);
my.pan = random( 360 ) - 0;
my.tilt = random( 90 ) - 0;
my.roll = random( 360 ) - 0;
my.alpha -= 3 *time_step;
wait (1);
}
ent_remove (my);
}
Finally, these functions will handle both the spell it's self, and the trail of the spell, and then fade both as I need them to fade:
Code:
function chargebolt_particlesinit (PARTICLE *p)
{
p.blue = 232.805;
p.green = 77.602;
p.red = 181.070;
p.bmap = chargebolt_outer; //the effect bitmap
p.vel_x = random( 0.3 ) - 0.100 ;
p.vel_y = random( 0.3 ) - 0.100 ;
p.vel_z = random( 0.3 ) - 0.100 ;
p.size = 44.318;
p.alpha = 30;
p.gravity = 0;
p.lifespan = 1;
p.flags |= (BRIGHT | MOVE | TRANSLUCENT);
p.event = chargebolt_fade;
}
function chargebolt_trail(PARTICLE *p)
{
p.blue = 38;
p.green = 27;
p.red = 122;
p.vel_x = random( 0.199 ) - 0.100;
p.vel_y = random( 0.199 ) - 0.100;
p.vel_z = random( 0.199 ) - 0.100;
chargebolt_trail_vec.x = random(.2) - .1;
chargebolt_trail_vec.y = random(.2) - .1;
chargebolt_trail_vec.z = random(.2) - .1;
vec_normalize (chargebolt_trail_vec, .2);
vec_add (p.vel_x, chargebolt_trail_vec);
p.alpha = 36.230;
p.bmap = chargebolt_trail_bmp;
p.size = 5;
p.flags |= (BRIGHT | MOVE | TRANSLUCENT);
p.event = chargebolt_fade_trail;
}
function chargebolt_fade(PARTICLE *p)
{
p.alpha -= 30 *time_step;
if(p.alpha < 0) { p.lifespan = 0; }
}
function chargebolt_fade_trail(PARTICLE *p)
{
p.alpha -= 8 *time_step;
if(p.alpha < 0) { p.lifespan = 0; }
}
I dont really have time to go into all of the set up for the spell creation its self, but I believe if you study the way I've handled this event, then you wil understand. Pay close attention the vectors. The spell knows where to go, because it constantly changes direction towards your target. (remember the vector in the prior marker function, for orientation?)
Here is another bit of coding, with the exact same principle, however it is for melee, and uses different techniques, but the same type of vectors:
Code:
function weapon_impact01_create()
{
vec_for_vertex (weapon_impact02_vec, item_temp[10], 5);
weapon_impact01_mdl = ent_create ("weapon_impact01.mdl", weapon_impact01_vec, weapon_impact01_init);
ent_create("melee_effect02.tga", weapon_impact02_vec, weapon_impact02_init);
}
function weapon_impact01_init()
{
set (my, PASSABLE);
set (my, INVISIBLE);
weapon_impact01_countdown(); // ** NEW
while (weapon_impact01_counter == 1)
{
weapon_impact01_particles = 10;
wait (-.3);
effect(weapon_impact01_live,maxv(1,weapon_impact01_particles*time_step),my.x,nullvector);
vec_set (my.x, weapon_impact01_vec.x);
vec_set (my.z, weapon_impact01_vec.z + 100);
}
ent_remove (my);
}
function weapon_impact01_live(PARTICLE *p)
{
p.blue = 128 ;
p.green = 128 ;
p.red = 128 ;
p.skill_c = 0; p.skill_b = 0;
p.bmap = weapon_impact01_bmap; //the effect bitmap
p.vel_x = random( 0 ) - 0 ;
p.vel_y = random( 0 ) - 0 ;
p.vel_z = random( 0 );
p.size = 70 ;
p.alpha = 20 ;
p.gravity = 0 ;
p.flags |= (MOVE | TRANSLUCENT);
p.event = weapon_impact01_fade;
}
function weapon_impact01_fade(PARTICLE *p)
{
if(p.size > 0) { p.size -= -3.063 *time_step; }else{p.lifespan = 0; }
p.alpha -= 3.346 *time_step;
if(p.alpha < 0) { p.alpha = 0; p.lifespan = 0; }
p.skill_b += 0.05;
p.skill_c = 4.348 - p.skill_b;
if(p.skill_c < 0) { p.lifespan = 0; }
}
function weapon_impact02_init()
{
set (my,BRIGHT);
set (my, PASSABLE);
my.scale_x = .5;
my.scale_y = .5;
while (my.scale_x > .1)
{
vec_set (my.x, weapon_impact02_vec.x);
my.scale_x -= .08 *time_step;
my.scale_y -= .08 *time_step;
my.alpha -= 10 *time_step;
wait (1);
}
ent_remove (my);
}
Now I realized there are a TON AND A HALF of variables, skills, etc. that you will ahve to define inorder to use this code, and I don't really have the time to go through and pick out all of those defines, so I will jsust go ahead and post the ones im sure match, if I forget some, just let me know, heh. You can search through and find the ones you need to make it all happen for you.
There are also ALOT of things that this will ahve to tie into in order to work for you. Such as the enemy AI, player code, etc. If you ahve more questions please feel free to ask. Just remember to be very specific, and clear. And also please note that as I've stated before, after my games upcoming release. I will be concentrating on creating a FULL_SCALE RPG tutorial with all of the fixings. Alot of things will be answered in that, I promise. Here is the defines:
Code:
BMAP* chargebolt_tracer_bmp = "ray_blue.tga";
BMAP* chargebolt_outer = "sun_blue.tga";
BMAP* chargebolt_clk = "ui_splfold_button_chargebolt_clk.tga";
BMAP* chargebolt_nrml = "ui_splfold_button_chargebolt_nrml.tga";
BMAP* chargebolt_ovr = "ui_splfold_button_chargebolt_ovr.tga";
BMAP* chargebolt_trail_bmp = "plasmaglow_blue.tga";
var weapon_impact01_particles = 1;
var weapon_impact01_counter;
var trailpos1[3];
var trailpos2[3];
var trailpos3[3];
var trailpos4[3];
var trailpos5[3];
var trailpos6[3];
var trailpos7[3];
var trailpos8[3];
var trailpos9[3];
var trailpos10[3];
var speed=12;
var trail_length=1; // adjust length of the trails
var marker_pointer; //stores the pointer to selected object
var marker_toggle; //toggles the marker on and off for object selection
var combat_mode = 0; // player is either in combat or not
var enemy_switching = 0; // makes sure you can't select more than one enemy, var distance_to_ground = 0; // the distance between player's origin and the ground
var spell_speed = 0; //speed of the spells cast
var attack_percentage = 0; //attack animations
var dist = 0; // enemy 1 path variables
var vLastPos[3];
var vDir[3];
var charge_bolt = 0; //spell toggle
var charge_bolt_countdown = 0;
var chargebolt_particles = 1;
var health_barslidex_mountain_qualix;
var health_barslidey_mountain_qualix;
var adren_barslidex_mountain_qualix;
var casting_spell;
var bodytracerparticles = 0; //number of particles used for this effect
var tracer_counter = 0;
var mountain_qualix_spawnpoint01_enemycount = 0;
var mountain_qualix_spawnpoint01_positioning [15] = {445,-10,740,775,350,740,930,-430,740,360,-250,740,-130,316,740};
var mountain_qualix_index = 0;
var mountain_qualix_sp01_max = 1;
var mountain_qualix_id = 0;
var marker_tga_id = 0;
var enemy_spawn_init_countdown = 0;
/// variables that help to figure out combat damage
var damage = 0;
var spell_damage = 0;
var disengage_combat;
VECTOR sword_base; //item_sword_00001 vertex position
vec_zero (sword_base);
VECTOR sword_tip; //item_sword_00001 vertex position
vec_zero (sword_tip);
VECTOR trace_dist; //tracing limits
vec_zero (trace_dist);
VECTOR movement_speed; // player's movement speed
vec_zero (movement_speed);
VECTOR temp2; //players tempz
vec_zero (temp2);
VECTOR temp_enemy; //enemies temp direction
vec_zero (temp_enemy);
VECTOR trace_down; //enemies temp direction
vec_zero (trace_down);
VECTOR combat_pan; //players pan during combat
vec_zero (combat_pan);
VECTOR chargebolt_vector; //players pan during combat
vec_zero (chargebolt_vector);
VECTOR chargebolt_trail_vec;
vec_zero (chargebolt_trail_vec);
VECTOR temp_spell01;
vec_zero (temp_spell01);
VECTOR temp_spell02;
vec_zero (temp_spell02);
VECTOR spell_tracer_temp01;
vec_zero (spell_tracer_temp01);
VECTOR spell_tracer_temp02;
vec_zero (spell_tracer_temp02);
VECTOR spell_tracer_temp03;
vec_zero (spell_tracer_temp03);
VECTOR spell_tracer_temp04;
vec_zero (spell_tracer_temp04);
VECTOR spell_tracer_temp05;
vec_zero (spell_tracer_temp05);
VECTOR spell_tracer_temp06;
vec_zero (spell_tracer_temp06);
VECTOR spell_tracer_temp07;
vec_zero (spell_tracer_temp07);
VECTOR spell_tracer_temp08;
vec_zero (spell_tracer_temp08);
VECTOR spell_tracer_temp09;
vec_zero (spell_tracer_temp09);
VECTOR spell_tracer_temp10;
vec_zero (spell_tracer_temp10);
VECTOR mountain_qualix_spawnpoint01_vector;
vec_zero (mountain_qualix_spawnpoint01_vector);
VECTOR mountain_qualix_speed;
vec_zero (mountain_qualix_speed);
VECTOR mountain_qualix_infront;
vec_zero (mountain_qualix_infront);
VECTOR mountain_qualix_vec01;
vec_zero (mountain_qualix_vec01);
VECTOR mountain_qualix_vec02;
vec_zero (mountain_qualix_vec02);
VECTOR mountain_qualix_vec03;
vec_zero (mountain_qualix_vec03);
VECTOR mountain_qualix_vec04;
vec_zero (mountain_qualix_vec04);
VECTOR mountain_qualix_vec05;
vec_zero (mountain_qualix_vec05);
VECTOR mountain_qualix_movement_speed;
vec_zero (mountain_qualix_movement_speed);
VECTOR temp3;
vec_zero (temp3);
VECTOR spawn_point01_vector;
vec_zero (spawn_point01_vector);
VECTOR spawn_point01_speed;
vec_zero (spawn_point01_speed);
VECTOR enemy_ground_vector;
vec_zero (enemy_ground_vector);
//melee special effects vectors
VECTOR weapon_impact01_vec;
vec_zero (weapon_impact01_vec);
VECTOR weapon_impact02_vec;
vec_zero (weapon_impact02_vec);
//Spell effect vectors
VECTOR chargebolt_vector02;
vec_zero (chargebolt_vector02);
ENTITY* mountain_qualix;
ENTITY* spawn_point01_locater;
ENTITY* selection_marker;
ENTITY* spell_chargebolt = "test_spell.mdl";
ENTITY* spell_chargebolt_shell01 = "chargebolt_outershell.mdl";
ENTITY* spell_chargebolt_shell02 = "chargebolt_aftereffect.mdl";
ENTITY* weapon_impact01_mdl = "weapon_impact01.mdl";
STRING* enemy_mountain_qualix = "Moujtain_Qualix.mdl";
Finally, you will of course need to set up function prototypes for all of the need functions.
I hope this helps!
Jesse