Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411509 Posts in 69375 Topics- by 58430 Members - Latest Member: Jesse Webb

April 26, 2024, 11:05:15 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperDesignSkill [Quad]Tree for your game!
Pages: [1]
Print
Author Topic: Skill [Quad]Tree for your game!  (Read 795 times)
danovo
Level 0
**


(Not an actual photograph)


View Profile
« on: January 17, 2018, 01:53:50 AM »

Hello there!

I was making a game based on the idea of quadtrees and came up with a neat little idea that hopefully some of you will be able to use. The thing is to make a recursive skill tree:

The idea is you can either choose to improve a general area, like "Movement", or something really specific, like "Movement > Run > Control". Each 'upgrade' has the same amount of points distributed over different area, so the more specific you get, the more points that go there. Also, the code is set to not allow overlaps, which leads to some interesting decisions, but it can be easily changed.

I don't know how to attach files so here's the full code. Just copy paste it into a .html file to test it for yourself. (Beware, the implementation is barebones and sketchy, if you actually plan to use this in an actual game you'll probably want to rewrite it)

Code:
<canvas id="gc" width="600" height="800"></canvas>
<script>
window.onload=function() {
    canvas=document.getElementById("gc");
    ctx=canvas.getContext("2d");
    if (document.addEventListener) {
document.addEventListener("mousewheel", MouseWheelHandler, false);
document.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
} else {
        document.attachEvent("onmousewheel", MouseWheelHandler);
    }
    
    canvas.oncontextmenu = function(e) {
        e.preventDefault();
    }    
    canvas.onmousedown = function (e) {
        window.focus();
        mouse.button  = e.which;
        mouse.px      = mouse.x;
        mouse.py      = mouse.y;
        var rect      = canvas.getBoundingClientRect();
        mouse.x       = e.clientX - rect.left,
        mouse.y       = e.clientY - rect.top,
        mouse.down    = true;
        e.preventDefault();
        e.stopPropagation();
        e.target.style.cursor = 'default';
        if (mouse.button == 1 && mouse.inboard) {
            if (ALLOW_OVERLAP || !overlaps(mousePoint)) {
                curpoints.push(mousePoint);
            }
        } else if (mouse.button == 3 && mouse.inboard) {        
            if (overlaps(mousePoint)) {
                curpoints = curpoints.filter(function(el) {
                    return !overlaps(el, [mousePoint]);
                });
            }
        }
    };

    canvas.onmouseup = function (e) {
        mouse.down = false;
        if (e)
            e.preventDefault();
    };

    canvas.onmousemove = function (e) {
        mouse.px  = mouse.x;
        mouse.py  = mouse.y;
        var rect  = canvas.getBoundingClientRect();
        mouse.x   = e.clientX - rect.left,
        mouse.y   = e.clientY - rect.top,
        mouse.inboard = mouse.x >= BOARD.x && mouse.x < BOARD.x + BOARD.w &&
                        mouse.y >= BOARD.y && mouse.y < BOARD.y + BOARD.h;
        e.preventDefault();
    };
    requestAnimationFrame(run);
}

function MouseWheelHandler(e) {
    // cross-browser wheel delta
    var e = window.event || e;
    var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
    zoom += delta;
    zoom = Math.min(MAX_ZOOM, Math.max(zoom, 1));
    /*e.preventDefault();
    e.stopPropagation();*/
    return false;
}

var mouse = {
    down: false,
    button: 1,
    x: 0,
    y: 0,
    px: 0,
    py: 0,
    inboard: false
};
var canvas;
var ctx;

var MAX_ZOOM = 3;
var BOARD = {x: 0, y: 0, w: 600, h:600};
var ALLOW_OVERLAP = false;
var zoom = 3;
var mousePoint = [];
var curpoints = [];

var DESCRIPTIONS = {
    0: {
        0: {
            0:"Duration",
            1:"Radius",
            2:"Cost",
            3:"Damage",
            "name":"Grenades"},
        1: {
            0:"Recharge",
            1:"Range",
            2:"Cost",
            3:"Damage",
            "name":"Rifles"},
        2: {
            0:"Spike Trap",
            1:"Bait",
            2:"Landmine",
            3:"Shark attack!",
            "name":"Specials"},
        3: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
    "name":"Weapons"},
    
    1: {
        0: {
            0:"Speed",
            1:"Duration",
            2:"Stamina",
            3:"Control",
            "name":"Run"},
        1: {
            0:"Speed",
            1:"Duration",
            2:"Cost",
            3:"Control",
            "name":"Jump"},
        2: {
            0:"Speed",
            1:"Recharge",
            2:"Range",
            3:"Damage",
            "name":"Teleport"},
        3: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
    "name":"Movement"},
    
    2: {
        0: {
            0:"More Health!",
            1:"More Health!",
            2:"More Health!",
            3:"More Health!",
            "name":"Health"},
        1: {
            0:"Cost",
            1:"Regen",
            2:"Effectivity",
            3:"Amount",
            "name":"Armor"},
        2: {
            0:"HP / s",
            1:"Vampire",
            2:"Find Potions",
            3:"SAMPLE_TEXT",
            "name":"Regeneration"},
        3: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
    "name":"Life"},
    
    3: {
        0: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
        1: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
        2: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
        3: {
            0:"SAMPLE_TEXT",
            1:"SAMPLE_TEXT",
            2:"SAMPLE_TEXT",
            3:"SAMPLE_TEXT",
            "name":"SAMPLE_TEXT"},
    "name":"SAMPLE_TEXT"},
}

var run = function(time) {
    ctx.beginPath();
    ctx.fillStyle="white";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle="black";
    ctx.rect(BOARD.x, BOARD.y, BOARD.w, BOARD.h);
    ctx.moveTo(BOARD.x + BOARD.w/2, BOARD.y);
    ctx.lineTo(BOARD.x + BOARD.w/2, BOARD.y + BOARD.h);
    ctx.moveTo(BOARD.x, BOARD.y + BOARD.h/2);
    ctx.lineTo(BOARD.x + BOARD.w, BOARD.y + BOARD.h/2);
    ctx.stroke();
    
    mousePoint = pointPath(mouse.x, mouse.y, zoom);
    curpoints.forEach(function(el) {drawQuad(el);});
    if (mouse.inboard) {
        var col = 'rgba(0,0,0,' + Math.pow((mousePoint.length / MAX_ZOOM),1.6) + ')';
        if (!ALLOW_OVERLAP && overlaps(mousePoint))
            col = 'rgba(255,0,0,0.3)';
        drawQuad(mousePoint, col);
        
        var text = "";
        var desc = DESCRIPTIONS;
        for (var i = 0; i < mousePoint.length; i++) {
            text += " > " + (desc[mousePoint[i]].name || desc[mousePoint[i]]);
            desc = desc[mousePoint[i]];
        }
        ctx.font = "30px Arial";
        ctx.fillStyle = "black";
        ctx.textBaseline="top";
        ctx.fillText(text,BOARD.x, BOARD.y + BOARD.h);
    }
    requestAnimationFrame(run);
}

// Returns the path to reach a given point
function pointPath(px, py, d) {
    var x = BOARD.x;
    var y = BOARD.y;
    var w = BOARD.w;
    var h = BOARD.h;
    var z = 0;
    var path = [];
    while (z < d && px >= x && px < x+w && py >= y && py < y+h) {
        if (px < x+w/2) {
            if (py < y+h/2) {
                path.push(0);
            } else {
                path.push(2);
                y += h/2;
            }
        } else {
            if (py < y+h/2) {
                path.push(1);
                x += w/2;
            } else {
                path.push(3);
                x += w/2;
                y += h/2;
            }
        }
        w /= 2;
        h /= 2;
        z += 1;
    }
    return path;
}

function drawQuad(point, color) {
    var x = BOARD.x;
    var y = BOARD.y;
    var w = BOARD.w;
    var h = BOARD.h;
    for (var i = 0; i < point.length; i++) {
        ctx.beginPath();
        ctx.rect(x,    y,     w/2,h/2);
        ctx.rect(x+w/2,y,     w/2,h/2);
        ctx.rect(x+w/2,y+h/2, w/2,h/2);
        ctx.rect(x,    y+h/2, w/2,h/2);
        ctx.stroke();
        if (point[i] == 1 || point[i] == 3) {
            x += w/2
        }
        if (point[i] == 2 || point[i] == 3) {
            y += w/2
        }
        w /= 2;
        h /= 2;
    }
    ctx.beginPath();
    var col = color || 'rgba(0,0,0,' + Math.pow((point.length / MAX_ZOOM),1.6) + ')';
    ctx.fillStyle = col;
    ctx.fillRect(x,y,w,h);
    ctx.closePath();
}

function overlaps(value, check) {
    check = check || curpoints;
    return check.reduce(function(cur, el) {
        var smaller = Math.min(el.length, value.length);
        return cur + (el.slice(0, smaller).toString() == value.slice(0, smaller).toString());
    }, 0);
}

if (!Array.prototype.hasOwnProperty("last")) {
  Object.defineProperty(Array.prototype, "last", {
    get: function() {
      return this[this.length - 1];
    },
    set: function(val) {
      this[this.length - 1] = val;
    }
  });
}
</script>
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic