GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2019-08-09 11:20:24

Shakane
Member
Registered: 2019-08-08
Posts: 3

Novice deploying tooltip ctrls for spells and skills

Hello all.  Firstly want to thank Xot and others involved at GMLscripts.com for providing such useful script examples for us to build with and also providing discussion here as well.  Very much appreciated by a novice C++ or GML developer such as myself.  What i lack in talent i attempt to make up through persistence and pressure. 

Note to Xot - if its ok i'll put a follow up post in this thread with the code of each of the scripts i listed here for others to reference? Is there a way to expand or collapse the code annotations in these threads?

i'm am solo developing a 24bit fantasy roguelike, turn based game ( http://ocult.ca - for zipped launchable exe and currently have char/mob stats and basic melee and simple ranged combat mechanics and AI, equip inventory / skill inventory and general UI (action bar and xp meters) development in place.  A lot of the current code i've developed was built upon the basic rogue-lock package available on the gamemaker market place.  I found it useful enough to get started and self teach a limited understanding of GML.  At least to get as far as i have. 

Up to this point i've managed to plow through most issues of my own accord. However, i'm now faced with structuring and establishing a list of spells and skills to be made available on the activation bar.   Meaning that tool tip controls and targeting have been burning in my brain. I found and have been testing and implementing the following gmlscripts.com scripts that i felt would help set the ground work for this part of combat mechanic. 

I believe there used to be some discussion here about the following scripts but have been unable to locate some of it (or should have looked harder).  Not sure if all of these scripts are still available here, but i have them and can repost them here if ok or if anyone needs them.  I've been away from development for about a year so i'm sorta picking up the pieces and hoping for some suggestions on developing good tooltip controls and targeting for my game.  (ie: which scripts to use for which skills or spells).  As i've only just started to implement the scripts to test their function i thought it would be a good time to define where i'm at with my understanding of their function as well as which i'm currently using and how.   Feel free to comment on proper use of these scripts, how i'm going wrong or ways i could use these best to suit my needs in this game.  Please remember im pretty new to this code. wink I'll try to keep up. I currently have the target_list functioning and generating draw output for mobs in targeting HUD.   

Targeting GML scripts here
Expand///instance_nth_nearest(x, y, obj, n);
//
//  Returns the id of the nth nearest instance of an object
//  to a given point or keyword "noone" if none is found.
//
//      x,y       point coordinates, real
//      obj       object index, or keyword "all", real
//      n         proximity, real
//
/// GMLscripts.com/license

/*
Copyright (c) 2007-2014, GMLscripts.com

This software is provided 'as-is', without any express or implied 
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it 
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.

  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.
*/

{
    var pointx,pointy,object,n,list,nearest;
    pointx = argument0;
    pointy = argument1;
    object = argument2;
    n = argument3;
    n = min(max(1,n),instance_number(object));
    list = ds_priority_create();
    nearest = noone;
    with (object) ds_priority_add(list,id,distance_to_point(pointx,pointy));
    repeat (n) nearest = ds_priority_delete_min(list);
    ds_priority_destroy(list);
    return nearest;
}
Expand///instance_nearest_list(x, y, obj)
/*
 * Returns a list of instances in nearest order
 */
var xx = argument0;
var yy = argument1;
var obj = argument2;
var list = ds_list_create(); //initialise the list of objects
var inst;
do
{
    //Find the instance nearest to the x & y position
    inst = instance_nearest(xx, yy, obj);
    //If an instance is found, add it to the list
    //and deactivate the instance. It'll be
    //reactivated later.
    if (inst != noone)
    {
        ds_list_add(list, inst);
        instance_deactivate_object(inst);
    }
}
until (inst == noone); //break the loop when there's no more instances

//Reactivate the deactivated objects
for (var i = 0; i < ds_list_size(list); i++)
{
    instance_activate_object(list[|i]);
}

//Return the list of instances
return list;
Expand///instance_list_random(object)
/*
 * Returns a list containing all the instances of an object, in a random order.
 *
 * Example use:
 *
   var list = instance_list_random(objPlayer);
   for (var i = 0; i < ds_list_size(list); i++)
   {
      with (list[|i]) //destroy all players, in a random order
      {
          instance_destroy();
      }
   }
 *
 */
var list = ds_list_create();
var obj = argument0;
with (obj)
{
    ds_list_add(list, id);
}
ds_list_shuffle(list);
return list;
Expand//scr_collision_circle_list(x1,y1,radius,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given circle, or noone if no instances found.
//
//      x1,y1       center of the collision circle (filled), real
//      radius      radius of the collision circle (filled), real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,radius,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    radius = argument2;
    obj = argument3;
    prec = argument4;
    notme = argument5;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_circle(x1,y1,radius,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand//scr_collision_line_list(x1,y1,x2,y2,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given line, or noone if no instances found.
//
//      x1,y1       start point of the collision line, real
//      x2,y2       end point of the collision line, real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,x2,y2,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    x2 = argument2;
    y2 = argument3;
    obj = argument4;
    prec = argument5;
    notme = argument6;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_line(x1,y1,x2,y2,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand//scr_collision_rectangle_list(x1,y1,x2,y2,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given rectangle, or noone if no instances found.
//
//      x1,y1       first corner of the collision rectangle (filled), real
//      x2,y2       opposite corner of the collision rectangle (filled), real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,x2,y2,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    x2 = argument2;
    y2 = argument3;
    obj = argument4;
    prec = argument5;
    notme = argument6;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_rectangle(x1,y1,x2,y2,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand/// scr_instance_place_list(x,y,obj)
//
//  Returns a list data structure populated with the ids of instances 
//  which would collide with the current instance if it were placed 
//  at a given position, or noone if no instances found.
//
//      x,y         placement of current instance, real
//      obj         object to check for collision (or all), real
//
/// GMLscripts.com/license
{
    var x1,y1,obj,dsid,this,that,i;
    x1 = argument0;
    y1 = argument1;
    obj = argument2;
    dsid = ds_list_create();
    this = id;
    with (obj) {
        that = id;
        with (this) {
            i = instance_place(x1,y1,that);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand/// scr_TARGET_Change(up/down);

// Change the target that is highlighted currently by moving
// up or down the target list. Set it to "true" to move down the list
// and "false" to move up the list.

if argument0
{
e_pos++;                                        //Adds one onto the position within the list...
if e_pos == e_num e_pos=0;                      //If the position is more than the list, goes back to the top of the list...
e_cur = ds_list_find_value(target_list, e_pos); //Sets the variable "e" to the selected enemy from the list position...

//The following checks to see if there is no enemy and if there is none
//moves up a position until one is found or the beginning of the list is reached...

var count = 0;
while (e_cur == 0 && count <= e_num)
    {
    e_pos++;
    count++;
    if e_pos == e_num e_pos = 0;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{

// Same as above, only moving down the list...

e_pos--;
if e_pos < 0 e_pos = e_num - 1;
e_cur = ds_list_find_value(target_list, e_pos); 
var count = 0;
while (e_cur == 0 && count <= e_num)
    {
    e_pos--;
    count++;
    if e_pos < 0 e_pos = e_num - 1;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
Expand/// scr_TARGET_Cleanup();

//Destroy our list structure to free the memory it uses.
ds_list_destroy(target_list);
Expand/// scr_TARGET_Draw(instance, colour, view_offset_x, view_offset_y);

// The instance ID taken by this script is the ID of the instance that you want to draw the information for.
// Since these scripts are not only for a player object, but can be used in enemies or other types of object
// the draw script has to adapt to this.

// Note that this script is presented as part of the package more as a tool to show what information is required 
// and is probably not what you require in your game. The importnat thing to note is that you are going through
// the list of targets held in the player or other object "target_list", using the variables for that instance
// which deal with the list and it's details. The variables are:
//
// target_list  - the ds_list that holds the targets
// e_num        - the total size of the target list
// e_pos        - the currently selected target in the target list
// e_cur        - the instance ID of the currently selected target
// e_rng        - the range of the instance being used
//
// Your PARENT objects should also have a "name" variable if you want to give a graphic list display with 
// text for the targets. "name" can be any string you wish and will be displayed with a highlight for the 
// current target.

// Using these variables you can create your own draw scripts from a controller object, supplying the required 
// ID for the instance to get the variables from. Use the seperate parts that are given in this script as a 
// template for your own. 


///

// Set some basic draw colours and stuff
draw_set_color(argument1);
draw_set_font(fnt_txt_value2);

// Set up vars for this script
var enemy_id = 0;                   // will hold the ID of the enemy and then the text for their name if found
var inst = argument0;               // get the ID of the instance that is going to supply the variables for drawing

if instance_exists(inst)
{
// get the necessary vars from the instance being used
var li = inst.target_list;
var en = inst.e_num;
var ep = inst.e_pos;
var ec = inst.e_cur;
var er = inst.e_rng;

var x_off = argument2;      //View offsets, more for this demo than anything else.
var y_off = argument3;


// This loop checks the list to see if it contains an instance ID or the instance exists and then sets some text and 
// displays it if there is. If not it displays default text. It will show all the currently available targets as well 
// as marking which target you are currently tracking.
// NOTE! the PARENT for the targets should have a "name" variable that must have some text to display, although you can 
// supply an empty string "" and have it simpy show nothing, or remove that part altogther and draw what you require!

for (var i = 0; i < en; i++;)
    {
    enemy_id = ds_list_find_value(li, i);               // assign the list value to a variable...
    if enemy_id == 0                                    // check the instance is valid and exists
        {
        enemy_id = "No Enemy";                          // If no enemy is found or exists set the text to a default value
        }
    else
        {
        if instance_exists(enemy_id)                    // list has an instance, so check it exists
            {
            enemy_id = string(i + 1) + " " + ds_list_find_value(li, i).name; //get enemy name for HUD...
            }
        else
            {
            enemy_id = "Destroyed!";                    // instance doesn't exists so set text to destroyed until the list is updated next
            }
        }
    draw_text(view_xview[0] + x_off, view_yview[0] + (y_off * i)+48, enemy_id); //Draw text with enemy name...
    
    //This next part simply draws a rectangle around the currently targeted enemy name in the HUD...
    if ep == i
        {
        draw_set_alpha(0.5);
        //draw_rectangle(view_xview[0] + x_off, view_yview[0] + y_off  * i, view_xview[0] + x_off + 120, view_yview[0] + y_off * i, false);
        draw_set_alpha(1);
        draw_rectangle(view_xview[0] + x_off-24, view_yview[0] + (y_off * i)+24, view_xview[0] + x_off + 50, view_yview[0] + (y_off  * i)+48, true);
        
        }
    }

// This part will now draw a circle around the current target (if there is one) and then draw 
// a line from the instance being used for drawing to the target.
if ec != 0 && instance_exists(ec)
    {
    var pd = point_direction(ec.x+12, ec.y+12, inst.x+12, inst.y+12);
    var xx = lengthdir_x(0, pd);
    var yy = lengthdir_y(0, pd);
    draw_line(inst.x+12, inst.y+12, ec.x + xx+12, ec.y + yy+12);
    draw_circle(ec.x+12, ec.y+12, 12, true);
    }

// This part is more for the demo than anything else and draws a circle indicating the range for the 
// targetting system. It can be removed completely if necessary, or linked to a debug variable 
// so you can switch it on/off while testing.

//draw_set_alpha(0.25);

//Check for webGL in a browser
//if webgl_enabled == true || os_browser == browswer_not_a_browser
//{
//draw_set_blend_mode(bm_add);
//draw_circle_colour(inst.x, inst.y, er, c_black, argument1, false);
//draw_set_blend_mode(bm_normal);
//}
//else
//{
//draw_circle(inst.x, inst.y, er, true);
//}

// Reset drawing stuff...
draw_set_alpha(1);
draw_set_color(c_black);
}
Expand/// scr_TARGET_First(first/last);

// Select the first target on the list (true)
// or the last target on the list (false)

if argument0
{
e_pos = 0;                                        //Adds one onto the position within the list...
e_cur = ds_list_find_value(target_list, e_pos); //Sets the variable "e" to the selected enemy from the list position...
//The following checks to see if there is no enemy and if there is none
//moves up a position until one is found or the beginning of the list is reached...
var count = 0;
while (e_cur == 0 && count < e_num)
    {
    e_pos++;
    count++;
    if e_pos == e_num e_pos = 0;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{
// Same as above, only moving down the list...
e_pos = e_num - 1;
e_cur = ds_list_find_value(target_list, e_pos); 
var count = 0;
while (e_cur == 0 && count < e_num)
    {
    e_pos--;
    count++;
    if e_pos < 0 e_pos = e_num - 1;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
Expand/// scr_TARGET_Init(max_enemies, max_range, parent, update_speed);

e_pos = 0;                  // This will hold the position of the current target in the list.
e_num = argument0;          //  e_num = argument0; - This is the size of the list (or number of enemies on the list). If you want more (or less), just change this and the whole code should adapt.
e_cur = 0;                  // This will hold the current enemy id or noone if their is none.
e_rng = argument1;          // This is the detection range for targeting around the player object (as a radius).
update_speed = argument3;   // This is the update speed for the alarm that re-checks available targets
target_list = ds_list_create();     //Creates the main target list for use...

for (var i = 1; i <= e_num; i++;)    //This creates a loop the size of the variable e_num...
    {
    var near = instance_nth_nearest(x, y, argument2, i);
    if distance_to_object(near) < e_rng
        {
        ds_list_add(target_list, near.id); //Adds the id of the enemy object to the list...
        }
    else ds_list_add(target_list, 0);
    }

return true;
Expand/// scr_TARGET_Pick(parent, x, y);

// This script is used to permit the user to pick a target that is within range.
// You give the position to check and the object to check for (or the parent to check for).
// If the target is within range then it will be added to the current target list.

var tx = argument1;
var ty = argument2;
var t_id = instance_position(tx, ty, argument0);

// Make sure target object actually exists
if instance_exists(t_id)
{
// Check to see if the target is already in the target list and simnply select it if it is
var l_id = ds_list_find_index(target_list, t_id);
if l_id != -1
    {
    e_cur = t_id;
    e_pos = ds_list_find_index(target_list, t_id);
    }
else
    {
    // Target is not on the target list so check to see if it's in range
    if point_distance(x, y, t_id.x, t_id.y)< e_rng
        {
        // Target is in range, so check to see if the list is full and add it to the list (and sleect it)
        if ds_list_size(target_list) < e_num
            {
            ds_list_add(target_list, t_id);
            e_cur = t_id;
             e_pos = ds_list_find_index(target_list, t_id);
            }
        else
            {
            // Target list is full, so get the furthest away target on the list and remove it, then add the selected target at the end
            var max_d = 0;
            var max_i = -1;
            for (var i = 0; i < ds_list_size(target_list); i++;)
                {
                var temp_i = ds_list_find_value(target_list, i);
                var temp_d = distance_to_object(temp_i);
                if temp_d > max_d
                    {
                    max_d = temp_d;
                    max_i = i;
                    }
                }
            ds_list_delete(target_list, max_i);
            ds_list_add(target_list, t_id);
            e_cur = t_id;
            e_pos = ds_list_find_index(target_list, t_id);
            }
        }
    }
}
Expand/// scr_TARGET_Step();

// This script simply checks the range between the instance running the targeting 
// scripts and the current target, based on the given max range.

if instance_exists(e_cur)
{
// Check the range...
if distance_to_object(e_cur) > e_rng
    {
    // Enemy out of range, so skip to the next on the list
    e_cur = 0;
    // This loop goes down the list looking for the next enemy and setting the 
    // position and target accordingly...
    var count = 0;
    while(ds_list_find_value(target_list, e_pos) == 0 && count <= e_num)
        {
        e_pos--;
        if e_pos < 0 e_pos = e_num - 1;
        count++;
        }
    // Set the current target
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{
// No current target
e_pos = 0;
e_cur = 0;
}
Expand/// scr_TARGET_Update(parent, update);

// This is the update code... We need a temporary list to hold the old list values 
// so that any enemies, including the currently targeted one, maintain their position 
// in the list...

var update = argument1; // If true, the script will update list positions every iteration, but false will maintain the list as long as the enemies are in range
var temp_list = ds_list_create(); //A temp list to hold existing enemies...
var near = noone;

e_cur = ds_list_find_value(target_list, e_pos); //Find the current enemy if in list...

//This code adds all the enemies from the target_list within the given range to the temp_list...
var count = 0;
for (var i = 0; i < e_num; i++;)
{
var t_id = ds_list_find_value(target_list, i);
if instance_exists(t_id)
    {
    if point_distance(x, y, t_id.x, t_id.y)< e_rng
        {
        ds_list_add(temp_list, ds_list_find_value(target_list, i));
        count++;
        }
    }
}

// If the list has a full compliment of target ID's, no need to look for more... 
if count != i || update == true
{
// The list is not full, so look for more targets...
ds_list_sort(temp_list,true);           //Sorts the list in order of id...
ds_list_clear(target_list);             //Clear the target list...
ds_list_copy(temp_list,target_list);    //Add the enemies within range from the temp list to the target list...
ds_list_destroy(temp_list);             //delete the temp list...

for (i = 1; i <= e_num; i++;)           //This creates a loop the size of the variable num...
    {
    near = instance_nth_nearest(x, y, argument0, i); //Gets the id of enemy
    if distance_to_object(near) < e_rng
        {
        //If the id is not already in the target list then add it to the list
        //as long as the list is no longer than the variable "num" (see create event)...
        if ds_list_find_index(target_list, near) == -1 && ds_list_size(target_list) != e_num
            {
            ds_list_add(target_list, near);
            }
        }
    }

ds_list_sort(target_list, true);// Sorts the list in order of id... The sorting attempts to keep the list from changing too much each alarm call...
}

if ds_list_size(target_list) == 0
{
// If the list has no enemies in it set the "e_cur" variable to 0
e_cur = 0;
e_pos = 0;
repeat(10)
    {
    ds_list_add(target_list, 0);
    }
}
else
{
// make sure the list is full...
if ds_list_size(target_list) < e_num
    {
    var dif = e_num - ds_list_size(target_list);
    repeat(dif)
        {
        ds_list_add(target_list, 0);
        }
    }
// This code is to make sure that the "pos" variable and enemy selected
// don't change position within the list. It's not strictly necessary,
// but it means that there is less jumping around and confusion in the HUD...

if e_cur != 0 && e_pos != ds_list_find_index(target_list, e_cur)
    {
    if instance_exists(e_cur)
        {
        if distance_to_object(e_cur) < e_rng
            {
            var temp_pos = ds_list_find_index(target_list, e_cur);
            var temp_e = ds_list_find_value(target_list, e_pos);
            ds_list_replace(target_list, e_pos, e_cur);
            ds_list_replace(target_list, temp_pos, temp_e);
            }
        }
    }
}

// This finally sets the variable "pos"...
e_pos = ds_list_find_index(target_list, e_cur)

if e_pos == -1
{
e_pos = 0;
e_cur = 0;
}

scr_TARGET_Init  // currently testing scr_TARGET_Init in my obj_pointer create event
instance_list_random  // what i would use for large damage radius like hail or ice storm
instance_nearest_list  // finds closest objects in order
instance_nth_nearest  // good for selecting the nearest sequentially from an object ( possible chain lightning )
scr_collision_circle_list // perhaps used for spell like fire ball, acid fog, poison cloud or arc swing abilities (targeted on obj_player)
scr_collision_line_list  // possibly good for ray like spells or charge (bull rush) ability
scr_collision_rectangle_list  // iceberg spell (chunk of ice) or wall of fire
scr_instance_place_list // lists items that collide with an object (many uses)
scr_TARGET_Change // i'm testing in my obj_pointer step event to allow tabbing through target list.
scr_TARGET_Cleanup // called in obj_pointer Room End event
scr_TARGET_Draw // i'm testing in obj_pointer draw event
scr_TARGET_First // dynamically focuses on the first obj in list if one exists (not sure if needs to be used)
scr_TARGET_Pick // one object or monster (ie magic missle)
scr_TARGET_Step // i'm testing in my obj_pointer step event to allow tabbing through target list.
scr_TARGET_Update // i'm testing this script in my pointer Alarm event

I'll premise any assistance by saying that i'm currently using my obj_player to call an array to determine the active button on my action bar on the bottom.  At this time my obj_player step event is also checking the type of spell or ability and determining the mouse x and y to fire the selected spell (only MM or fireball atm) at it's target.  My plan is to call the same array by the obj_pointer and use its step event to determine type of skill/spell, then present the correct tooltip for it and generate the appropriate target list to be sent to the obj_player step event, which determines the effect and damage and distributes it to the mobs.  My player step event is getting large as i am checking for each action button setting (10 of them) and then checking the type there as well.  This is ok while i only have 2 spells in the list but I realize need to learn better structure by creating my own scripts to call for all the spells and skills. I still need to think out the best way to register the variables for the player while using scripts outside of obj_player step event for this.  i do plan to do so and i guess the sooner the better.  Honestly this should be my priority before moving ahead with implementing new scripts or my obj_player step event is gonna be huge. tongue

Once again, i can post these scripts here if they are not already and i really appreciate any insight or direction that can be offered.  This began as a simple experiment to see if i could actually make a roguelike game and turned into a pretty complicated project that i'm really enjoying.

Last edited by Shakane (2019-08-13 14:51:27)

Offline

#2 2019-08-10 02:21:01

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Novice deploying tooltip ctrls for spells and skills

Welcome to the forums, Shakane. I don't know that this is going to be a very fruitful place to get help. There is very little traffic here. GMC is a far better place to find general help. Or maybe r/gamemaker or their Discord.

If you need help with specific scripts posted on the site, I'll try to help you with them. Feel free to post as must as you need. I've added a simple, untested spoiler tag for you. You can see how it's used here: BBCode Help

Spoiler

It might even work.


Abusing forum power since 1986.

Offline

#3 2019-08-10 13:54:07

Shakane
Member
Registered: 2019-08-08
Posts: 3

Re: Novice deploying tooltip ctrls for spells and skills

Hey thx xot!  oddly i'm not struggling with anything specifically atm.  Although the fact that i'm building upon script that i didn't create from scratch has had its problems.  I've learned to obsess less and read more and when problems become overwhelming to make references and take a break.  Things seem simpler when i come back.

TBH i'm happy to have somewhere to organize and spill my thoughts and is ok if not much activity, as long as you don't mind xot?  The only thing i worry about is misrepresenting anything incorrectly here. 

Targeting GML scripts here
Expand///instance_nth_nearest(x, y, obj, n);
//
//  Returns the id of the nth nearest instance of an object
//  to a given point or keyword "noone" if none is found.
//
//      x,y       point coordinates, real
//      obj       object index, or keyword "all", real
//      n         proximity, real
//
/// GMLscripts.com/license

/*
Copyright (c) 2007-2014, GMLscripts.com

This software is provided 'as-is', without any express or implied 
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it 
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.

  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.
*/

{
    var pointx,pointy,object,n,list,nearest;
    pointx = argument0;
    pointy = argument1;
    object = argument2;
    n = argument3;
    n = min(max(1,n),instance_number(object));
    list = ds_priority_create();
    nearest = noone;
    with (object) ds_priority_add(list,id,distance_to_point(pointx,pointy));
    repeat (n) nearest = ds_priority_delete_min(list);
    ds_priority_destroy(list);
    return nearest;
}
Expand///instance_nearest_list(x, y, obj)
/*
 * Returns a list of instances in nearest order
 */
var xx = argument0;
var yy = argument1;
var obj = argument2;
var list = ds_list_create(); //initialise the list of objects
var inst;
do
{
    //Find the instance nearest to the x & y position
    inst = instance_nearest(xx, yy, obj);
    //If an instance is found, add it to the list
    //and deactivate the instance. It'll be
    //reactivated later.
    if (inst != noone)
    {
        ds_list_add(list, inst);
        instance_deactivate_object(inst);
    }
}
until (inst == noone); //break the loop when there's no more instances

//Reactivate the deactivated objects
for (var i = 0; i < ds_list_size(list); i++)
{
    instance_activate_object(list[|i]);
}

//Return the list of instances
return list;
Expand///instance_list_random(object)
/*
 * Returns a list containing all the instances of an object, in a random order.
 *
 * Example use:
 *
   var list = instance_list_random(objPlayer);
   for (var i = 0; i < ds_list_size(list); i++)
   {
      with (list[|i]) //destroy all players, in a random order
      {
          instance_destroy();
      }
   }
 *
 */
var list = ds_list_create();
var obj = argument0;
with (obj)
{
    ds_list_add(list, id);
}
ds_list_shuffle(list);
return list;
Expand//scr_collision_circle_list(x1,y1,radius,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given circle, or noone if no instances found.
//
//      x1,y1       center of the collision circle (filled), real
//      radius      radius of the collision circle (filled), real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,radius,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    radius = argument2;
    obj = argument3;
    prec = argument4;
    notme = argument5;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_circle(x1,y1,radius,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand//scr_collision_line_list(x1,y1,x2,y2,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given line, or noone if no instances found.
//
//      x1,y1       start point of the collision line, real
//      x2,y2       end point of the collision line, real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,x2,y2,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    x2 = argument2;
    y2 = argument3;
    obj = argument4;
    prec = argument5;
    notme = argument6;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_line(x1,y1,x2,y2,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand//scr_collision_rectangle_list(x1,y1,x2,y2,obj,prec,notme)
//
//  Returns a list data structure populated with the ids of instances 
//  colliding with a given rectangle, or noone if no instances found.
//
//      x1,y1       first corner of the collision rectangle (filled), real
//      x2,y2       opposite corner of the collision rectangle (filled), real
//      obj         object to check for collision (or all), real
//      prec        true for precise collision checking, bool
//      notme       true to ignore the calling instance, bool
//
/// GMLscripts.com/license
{
    var x1,y1,x2,y2,obj,prec,notme,dsid,i;
    x1 = argument0;
    y1 = argument1;
    x2 = argument2;
    y2 = argument3;
    obj = argument4;
    prec = argument5;
    notme = argument6;
    dsid = ds_list_create();
    with (obj) {
        if (!notme || id != other.id) {
            i = collision_rectangle(x1,y1,x2,y2,id,prec,false);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand/// scr_instance_place_list(x,y,obj)
//
//  Returns a list data structure populated with the ids of instances 
//  which would collide with the current instance if it were placed 
//  at a given position, or noone if no instances found.
//
//      x,y         placement of current instance, real
//      obj         object to check for collision (or all), real
//
/// GMLscripts.com/license
{
    var x1,y1,obj,dsid,this,that,i;
    x1 = argument0;
    y1 = argument1;
    obj = argument2;
    dsid = ds_list_create();
    this = id;
    with (obj) {
        that = id;
        with (this) {
            i = instance_place(x1,y1,that);
            if (i != noone) ds_list_add(dsid,i);
        }
    }
    if (ds_list_empty(dsid)) {
        ds_list_destroy(dsid);
        dsid = noone;
    }
    return dsid;
}
Expand/// scr_TARGET_Change(up/down);

// Change the target that is highlighted currently by moving
// up or down the target list. Set it to "true" to move down the list
// and "false" to move up the list.

if argument0
{
e_pos++;                                        //Adds one onto the position within the list...
if e_pos == e_num e_pos=0;                      //If the position is more than the list, goes back to the top of the list...
e_cur = ds_list_find_value(target_list, e_pos); //Sets the variable "e" to the selected enemy from the list position...

//The following checks to see if there is no enemy and if there is none
//moves up a position until one is found or the beginning of the list is reached...

var count = 0;
while (e_cur == 0 && count <= e_num)
    {
    e_pos++;
    count++;
    if e_pos == e_num e_pos = 0;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{

// Same as above, only moving down the list...

e_pos--;
if e_pos < 0 e_pos = e_num - 1;
e_cur = ds_list_find_value(target_list, e_pos); 
var count = 0;
while (e_cur == 0 && count <= e_num)
    {
    e_pos--;
    count++;
    if e_pos < 0 e_pos = e_num - 1;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
Expand/// scr_TARGET_Cleanup();

//Destroy our list structure to free the memory it uses.
ds_list_destroy(target_list);
Expand/// scr_TARGET_Draw(instance, colour, view_offset_x, view_offset_y);

// The instance ID taken by this script is the ID of the instance that you want to draw the information for.
// Since these scripts are not only for a player object, but can be used in enemies or other types of object
// the draw script has to adapt to this.

// Note that this script is presented as part of the package more as a tool to show what information is required 
// and is probably not what you require in your game. The importnat thing to note is that you are going through
// the list of targets held in the player or other object "target_list", using the variables for that instance
// which deal with the list and it's details. The variables are:
//
// target_list  - the ds_list that holds the targets
// e_num        - the total size of the target list
// e_pos        - the currently selected target in the target list
// e_cur        - the instance ID of the currently selected target
// e_rng        - the range of the instance being used
//
// Your PARENT objects should also have a "name" variable if you want to give a graphic list display with 
// text for the targets. "name" can be any string you wish and will be displayed with a highlight for the 
// current target.

// Using these variables you can create your own draw scripts from a controller object, supplying the required 
// ID for the instance to get the variables from. Use the seperate parts that are given in this script as a 
// template for your own. 


///

// Set some basic draw colours and stuff
draw_set_color(argument1);
draw_set_font(fnt_txt_value2);

// Set up vars for this script
var enemy_id = 0;                   // will hold the ID of the enemy and then the text for their name if found
var inst = argument0;               // get the ID of the instance that is going to supply the variables for drawing

if instance_exists(inst)
{
// get the necessary vars from the instance being used
var li = inst.target_list;
var en = inst.e_num;
var ep = inst.e_pos;
var ec = inst.e_cur;
var er = inst.e_rng;

var x_off = argument2;      //View offsets, more for this demo than anything else.
var y_off = argument3;


// This loop checks the list to see if it contains an instance ID or the instance exists and then sets some text and 
// displays it if there is. If not it displays default text. It will show all the currently available targets as well 
// as marking which target you are currently tracking.
// NOTE! the PARENT for the targets should have a "name" variable that must have some text to display, although you can 
// supply an empty string "" and have it simpy show nothing, or remove that part altogther and draw what you require!

for (var i = 0; i < en; i++;)
    {
    enemy_id = ds_list_find_value(li, i);               // assign the list value to a variable...
    if enemy_id == 0                                    // check the instance is valid and exists
        {
        enemy_id = "No Enemy";                          // If no enemy is found or exists set the text to a default value
        }
    else
        {
        if instance_exists(enemy_id)                    // list has an instance, so check it exists
            {
            enemy_id = string(i + 1) + " " + ds_list_find_value(li, i).name; //get enemy name for HUD...
            }
        else
            {
            enemy_id = "Destroyed!";                    // instance doesn't exists so set text to destroyed until the list is updated next
            }
        }
    draw_text(view_xview[0] + x_off, view_yview[0] + (y_off * i)+48, enemy_id); //Draw text with enemy name...
    
    //This next part simply draws a rectangle around the currently targeted enemy name in the HUD...
    if ep == i
        {
        draw_set_alpha(0.5);
        //draw_rectangle(view_xview[0] + x_off, view_yview[0] + y_off  * i, view_xview[0] + x_off + 120, view_yview[0] + y_off * i, false);
        draw_set_alpha(1);
        draw_rectangle(view_xview[0] + x_off-24, view_yview[0] + (y_off * i)+24, view_xview[0] + x_off + 50, view_yview[0] + (y_off  * i)+48, true);
        
        }
    }

// This part will now draw a circle around the current target (if there is one) and then draw 
// a line from the instance being used for drawing to the target.
if ec != 0 && instance_exists(ec)
    {
    var pd = point_direction(ec.x+12, ec.y+12, inst.x+12, inst.y+12);
    var xx = lengthdir_x(0, pd);
    var yy = lengthdir_y(0, pd);
    draw_line(inst.x+12, inst.y+12, ec.x + xx+12, ec.y + yy+12);
    draw_circle(ec.x+12, ec.y+12, 12, true);
    }

// This part is more for the demo than anything else and draws a circle indicating the range for the 
// targetting system. It can be removed completely if necessary, or linked to a debug variable 
// so you can switch it on/off while testing.

//draw_set_alpha(0.25);

//Check for webGL in a browser
//if webgl_enabled == true || os_browser == browswer_not_a_browser
//{
//draw_set_blend_mode(bm_add);
//draw_circle_colour(inst.x, inst.y, er, c_black, argument1, false);
//draw_set_blend_mode(bm_normal);
//}
//else
//{
//draw_circle(inst.x, inst.y, er, true);
//}

// Reset drawing stuff...
draw_set_alpha(1);
draw_set_color(c_black);
}
Expand/// scr_TARGET_First(first/last);

// Select the first target on the list (true)
// or the last target on the list (false)

if argument0
{
e_pos = 0;                                        //Adds one onto the position within the list...
e_cur = ds_list_find_value(target_list, e_pos); //Sets the variable "e" to the selected enemy from the list position...
//The following checks to see if there is no enemy and if there is none
//moves up a position until one is found or the beginning of the list is reached...
var count = 0;
while (e_cur == 0 && count < e_num)
    {
    e_pos++;
    count++;
    if e_pos == e_num e_pos = 0;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{
// Same as above, only moving down the list...
e_pos = e_num - 1;
e_cur = ds_list_find_value(target_list, e_pos); 
var count = 0;
while (e_cur == 0 && count < e_num)
    {
    e_pos--;
    count++;
    if e_pos < 0 e_pos = e_num - 1;
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
Expand/// scr_TARGET_Init(max_enemies, max_range, parent, update_speed);

e_pos = 0;                  // This will hold the position of the current target in the list.
e_num = argument0;          //  e_num = argument0; - This is the size of the list (or number of enemies on the list). If you want more (or less), just change this and the whole code should adapt.
e_cur = 0;                  // This will hold the current enemy id or noone if their is none.
e_rng = argument1;          // This is the detection range for targeting around the player object (as a radius).
update_speed = argument3;   // This is the update speed for the alarm that re-checks available targets
target_list = ds_list_create();     //Creates the main target list for use...

for (var i = 1; i <= e_num; i++;)    //This creates a loop the size of the variable e_num...
    {
    var near = instance_nth_nearest(x, y, argument2, i);
    if distance_to_object(near) < e_rng
        {
        ds_list_add(target_list, near.id); //Adds the id of the enemy object to the list...
        }
    else ds_list_add(target_list, 0);
    }

return true;
Expand/// scr_TARGET_Pick(parent, x, y);

// This script is used to permit the user to pick a target that is within range.
// You give the position to check and the object to check for (or the parent to check for).
// If the target is within range then it will be added to the current target list.

var tx = argument1;
var ty = argument2;
var t_id = instance_position(tx, ty, argument0);

// Make sure target object actually exists
if instance_exists(t_id)
{
// Check to see if the target is already in the target list and simnply select it if it is
var l_id = ds_list_find_index(target_list, t_id);
if l_id != -1
    {
    e_cur = t_id;
    e_pos = ds_list_find_index(target_list, t_id);
    }
else
    {
    // Target is not on the target list so check to see if it's in range
    if point_distance(x, y, t_id.x, t_id.y)< e_rng
        {
        // Target is in range, so check to see if the list is full and add it to the list (and sleect it)
        if ds_list_size(target_list) < e_num
            {
            ds_list_add(target_list, t_id);
            e_cur = t_id;
             e_pos = ds_list_find_index(target_list, t_id);
            }
        else
            {
            // Target list is full, so get the furthest away target on the list and remove it, then add the selected target at the end
            var max_d = 0;
            var max_i = -1;
            for (var i = 0; i < ds_list_size(target_list); i++;)
                {
                var temp_i = ds_list_find_value(target_list, i);
                var temp_d = distance_to_object(temp_i);
                if temp_d > max_d
                    {
                    max_d = temp_d;
                    max_i = i;
                    }
                }
            ds_list_delete(target_list, max_i);
            ds_list_add(target_list, t_id);
            e_cur = t_id;
            e_pos = ds_list_find_index(target_list, t_id);
            }
        }
    }
}
Expand/// scr_TARGET_Step();

// This script simply checks the range between the instance running the targeting 
// scripts and the current target, based on the given max range.

if instance_exists(e_cur)
{
// Check the range...
if distance_to_object(e_cur) > e_rng
    {
    // Enemy out of range, so skip to the next on the list
    e_cur = 0;
    // This loop goes down the list looking for the next enemy and setting the 
    // position and target accordingly...
    var count = 0;
    while(ds_list_find_value(target_list, e_pos) == 0 && count <= e_num)
        {
        e_pos--;
        if e_pos < 0 e_pos = e_num - 1;
        count++;
        }
    // Set the current target
    e_cur = ds_list_find_value(target_list, e_pos);
    }
}
else
{
// No current target
e_pos = 0;
e_cur = 0;
}
Expand/// scr_TARGET_Update(parent, update);

// This is the update code... We need a temporary list to hold the old list values 
// so that any enemies, including the currently targeted one, maintain their position 
// in the list...

var update = argument1; // If true, the script will update list positions every iteration, but false will maintain the list as long as the enemies are in range
var temp_list = ds_list_create(); //A temp list to hold existing enemies...
var near = noone;

e_cur = ds_list_find_value(target_list, e_pos); //Find the current enemy if in list...

//This code adds all the enemies from the target_list within the given range to the temp_list...
var count = 0;
for (var i = 0; i < e_num; i++;)
{
var t_id = ds_list_find_value(target_list, i);
if instance_exists(t_id)
    {
    if point_distance(x, y, t_id.x, t_id.y)< e_rng
        {
        ds_list_add(temp_list, ds_list_find_value(target_list, i));
        count++;
        }
    }
}

// If the list has a full compliment of target ID's, no need to look for more... 
if count != i || update == true
{
// The list is not full, so look for more targets...
ds_list_sort(temp_list,true);           //Sorts the list in order of id...
ds_list_clear(target_list);             //Clear the target list...
ds_list_copy(temp_list,target_list);    //Add the enemies within range from the temp list to the target list...
ds_list_destroy(temp_list);             //delete the temp list...

for (i = 1; i <= e_num; i++;)           //This creates a loop the size of the variable num...
    {
    near = instance_nth_nearest(x, y, argument0, i); //Gets the id of enemy
    if distance_to_object(near) < e_rng
        {
        //If the id is not already in the target list then add it to the list
        //as long as the list is no longer than the variable "num" (see create event)...
        if ds_list_find_index(target_list, near) == -1 && ds_list_size(target_list) != e_num
            {
            ds_list_add(target_list, near);
            }
        }
    }

ds_list_sort(target_list, true);// Sorts the list in order of id... The sorting attempts to keep the list from changing too much each alarm call...
}

if ds_list_size(target_list) == 0
{
// If the list has no enemies in it set the "e_cur" variable to 0
e_cur = 0;
e_pos = 0;
repeat(10)
    {
    ds_list_add(target_list, 0);
    }
}
else
{
// make sure the list is full...
if ds_list_size(target_list) < e_num
    {
    var dif = e_num - ds_list_size(target_list);
    repeat(dif)
        {
        ds_list_add(target_list, 0);
        }
    }
// This code is to make sure that the "pos" variable and enemy selected
// don't change position within the list. It's not strictly necessary,
// but it means that there is less jumping around and confusion in the HUD...

if e_cur != 0 && e_pos != ds_list_find_index(target_list, e_cur)
    {
    if instance_exists(e_cur)
        {
        if distance_to_object(e_cur) < e_rng
            {
            var temp_pos = ds_list_find_index(target_list, e_cur);
            var temp_e = ds_list_find_value(target_list, e_pos);
            ds_list_replace(target_list, e_pos, e_cur);
            ds_list_replace(target_list, temp_pos, temp_e);
            }
        }
    }
}

// This finally sets the variable "pos"...
e_pos = ds_list_find_index(target_list, e_cur)

if e_pos == -1
{
e_pos = 0;
e_cur = 0;
}

I'm back to fixing resolution issues for now.  wink  thx again!

Last edited by Shakane (2019-08-13 14:59:25)

Offline

#4 2019-08-10 16:31:48

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Novice deploying tooltip ctrls for spells and skills

Thanks for reporting that copy/paste mistake in the help.

I see I failed to allow code tags inside spoilers. I was sure I tested that. I'll work on getting that and other block tags to work correctly inside the spoiler tag.

EDIT:

I've completely rewritten the spoiler tags.

Hopefully ...
Expand/// ... spoiler tags will work as expected now.

Last edited by xot (2019-08-10 17:19:33)


Abusing forum power since 1986.

Offline

#5 2019-08-11 20:14:38

Shakane
Member
Registered: 2019-08-08
Posts: 3

Re: Novice deploying tooltip ctrls for spells and skills

Seems to be functional IMO.  I appreciate it xot. 

I've patched my game so at least the minimized version will be testable now i think.  (update - Full screen seems good on all resolutions now) Also fixed some lighting issues by accident.  tongue   Zooming the view was causing me a lot of trouble with map/ui/shadow layer and wound up doing some things i shouldn't have. Nice to have that fixed and the UI drawing and scaling with the GUI properly on all the resolutions i tested. 

The targeting HUD is displaying and scaling nicely with the zoom now and i'm able to consistently test the targeting scripts a bit more.  I loaded a seed with my update that places the char in the room where i could scale and test HUD at its view location while trying to draw it in different ways and testing the view zoom. I was surprised that gui layer wasn't the answer.  Trial and error got me there tbh.  But i learned a bit more about calling functions of other objects from within an object along the way. 

Still slow progress.  Creating bugs as fast as i patch them.  lol

Last edited by Shakane (2019-08-12 21:58:22)

Offline

Board footer

Powered by FluxBB