Marabunta: 2D Ant Game

Game development specific discussions.
Post Reply
Boromir
Posts: 463
Joined: Apr 30, 2015 19:28
Location: Oklahoma,U.S., Earth,Solar System
Contact:

Marabunta: 2D Ant Game

Post by Boromir »

This is a simple army ant fighting game.
You control the knight in armor, using the w-a-s-d keys.
The only objective is to attack the ants with your torch by pressing the space key.
You can also cheat using the mouse.

Code: Select all

'=======================================================================
' Marabunta
' by Ezekiel Gutierrez
' 2023
'=======================================================================

#include "fbgfx.bi"
#include "GL/gl.bi"
using fb
const SCREEN_WIDTH = 640
const SCREEN_HEIGHT = 360
const SCALING = 2
screenres SCREEN_WIDTH, SCREEN_HEIGHT, 32
screencontrol(SET_GL_2D_MODE, OGL_2D_MANUAL_SYNC)
screencontrol(SET_GL_SCALE, SCALING)
screenres SCREEN_WIDTH, SCREEN_HEIGHT, 32, , 2 or 1
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)



const RAD_TO_DEG = 57.29577951308232
const DEG_TO_RAD = 0.017453292519943295769236907684886
const MAX_SPEED = 1.0

dim shared as double cos_table(359)
dim shared as double sin_table(359)

sub generate_lookup_tables()
    dim as double angle = 0
    
    for i as long = 0 to 359
        cos_table(i) = cos(angle)
        sin_table(i) = sin(angle)
        
        angle += DEG_TO_RAD
    next i
end sub

generate_lookup_tables()

function get_cos(angle as double) as double
    dim as long index = (angle * RAD_TO_DEG) mod 360
    while index < 0:index+=360:wend
    return cos_table(index)
end function

function get_sin(angle as double) as double
    dim as long index = (angle * RAD_TO_DEG) mod 360
    while index < 0:index+=360:wend
    return sin_table(index)
end function


dim shared as double timval,timevalold
dim shared as long editposit=1
timval=timer:timevalold=timval

dim shared as long frames,fps
dim shared as double prevtime

sub updatefps()
    timval=timer
    timval-=timevalold
    timevalold=timer
    timval*=60
    'fps counter
    frames+=1
    if timer-1>prevtime then fps=frames:frames=0:prevtime=timer
    
    'correction limit
    if timval<0 then timval=0
    if timval>10 then timval=10
end sub

'=======================================================================
' Types
'=======================================================================

type ant_type
    x as double
    y as double
    dx as double
    dy as double
    squashed as boolean
    angle as double
    declare sub update(ants() as ant_type, mouse_x as long, mouse_y as long, click as long, byref dead as long, byref score as long)
    declare sub render()
end type

type player_type
    x as double
    y as double
    swing_x as double
    swing_y as double
    angle as double
    torch_radius as double
    is_swinging as boolean
    swing_timer as double
    last_direction as long
    wiggle_offset as double
    wiggle_direc as boolean
    declare sub update(ants() as ant_type, mouse_x as long, mouse_y as long, click as long, byref dead as long, byref score as long)
    declare sub render()
end type

type level_type
    ants(any) as ant_type
    player as player_type
    dead as long
    score as long
    num_ants as long=1
    dim as long mouse_x, mouse_y, click
    declare sub initialize()
    declare sub update()
    declare sub render()
end type

'=======================================================================
' Routines
'=======================================================================

sub player_type.update(ants() as ant_type, mouse_x as long, mouse_y as long, click as long, byref dead as long, byref score as long)
    ' Attack ants with space key
    if multikey(&H39) andalso swing_timer > 30 then
        for i as long = 0 to ubound(ants)
            if ants(i).squashed = false then
                dim as long ant_dist_x = abs(ants(i).x - x)
                dim as long ant_dist_y = abs(ants(i).y - y)
                dim as long ant_distance = ant_dist_x * ant_dist_x + ant_dist_y * ant_dist_y

                if ant_distance <= 20 * 20 * 8 then
                    ants(i).squashed = true
                    score += 1
                    swap ants(dead), ants(i)
                    dead += 1
                end if
            end if
        next i
        swing_timer = 0
        is_swinging = true
    end if

    swing_timer += 1 * timval

    ' Move player with WASD keys
    if multikey(&H11) then ' W key
        y -= 1 * timval
        x += wiggle_offset
        last_direction = 0
    end if
    if multikey(&H1F) then ' S key
        y += 1 * timval
        x += wiggle_offset
        last_direction = 1
    end if
    if multikey(&H1E) then ' A key
        x -= 1 * timval
        y += wiggle_offset
        last_direction = 2
    end if
    if multikey(&H20) then ' D key
        x += 1 * timval
        y += wiggle_offset
        last_direction = 3
    end if

    ' Collide on screen edges
    if x < 0 then x = 0
    if x > SCREEN_WIDTH then x = SCREEN_WIDTH
    if y < 0 then y = 0
    if y > SCREEN_HEIGHT then y = SCREEN_HEIGHT

    ' Calculate vertical wiggle offset
    if wiggle_direc= true then wiggle_offset+=0.1*timval
    if wiggle_direc= false then wiggle_offset-=0.1*timval
    if wiggle_offset>=0.5 then wiggle_direc=false
    if wiggle_offset<=-0.5 then wiggle_direc=true

end sub

sub player_type.render()

    if is_swinging=true then
        angle += 0.2*timval

        select case last_direction
            case 0 ' Up
                if angle > 1.5 * 3.14159 then angle = 1.5 * 3.14159 -6.28: is_swinging = false
            case 1 ' Down
                if angle > 3.14159 * 0.5 then angle = 3.14159 *0.5 -6.28: is_swinging = false
            case 2 ' Left
                if angle > 3.14159 then angle = -3.14159: is_swinging = false
            case 3 ' Right
                if angle > 2 * 3.14159 then angle = 0: is_swinging = false
        end select

    end if
    swing_x = x + get_cos(angle) * torch_radius
    swing_y = y + get_sin(angle) * torch_radius
    
    dim as long torch_color = rgb(255, 200, 0)
    dim as long bundle_color = rgb(220, 120, 60)
    ' Draw torch swinging animation
    line (x, y)-(swing_x, swing_y), torch_color, bf
    ' Draw burning bundle at the end of the torch
    circle (swing_x, swing_y), 6, bundle_color, , , , f
    
    
    ' Draw knight in armor
    dim as long armor_color = rgb(100, 100, 100)
    dim as long helmet_color = rgb(50, 50, 50)
    dim as long sword_color = rgb(200, 200, 200)
    
    ' Body
    circle (x, y), 10, armor_color, , , , f
    ' Helmet
    circle (x, y - 10), 6, helmet_color, , , , f
    ' Arms
    line (x - 8, y - 2)-(x + 8, y - 2), armor_color
    ' Legs
    line (x - 4, y + 6)-(x - 2, y + 12), armor_color
    line (x + 4, y + 6)-(x + 2, y + 12), armor_color
    ' Sword
    line (x + 8, y + 6)-(x + 12, y - 2), sword_color
    
end sub

sub level_type.update()
    if dead>num_ants then initialize()
    
    getmouse mouse_x,mouse_y,, click
    mouse_x/=SCALING
    mouse_y/=SCALING
    player.update(ants(),mouse_x,mouse_y,click,dead,score)
    
    for i as long = 0 to ubound(ants)
        ants(i).update(ants(),mouse_x,mouse_y,click,dead,score)
    next
end sub

sub level_type.render()
    ' Draw background
    line (0, 0)-(SCREEN_WIDTH, SCREEN_HEIGHT), rgb(170, 140, 110) , bf

    ' Render ants
    for i as long = 0 to ubound(ants)
        ants(i).render()
    next
    ' Render player
    player.render()
    ' Draw foreground (score and fps)
    draw string (0, 0), "Score: " + str(score) + "    fps: " + str(fps), rgb(255, 255, 255)

    flip
end sub
    
sub level_type.initialize()
    num_ants*=2
    if num_ants>20000 then num_ants=20000
    redim ants(num_ants)
    dead=0
    for i as long = 0 to ubound(ants)
        ants(i).x = rnd * SCREEN_WIDTH
        ants(i).y = rnd * SCREEN_HEIGHT
        ants(i).dx = (rnd * 1.00)-(rnd * 1.00)
        ants(i).dy = (rnd * 0.30)-(rnd * 0.30)
        ants(i).squashed = false
    next i
    
    player.x = SCREEN_WIDTH / 2
    player.y = SCREEN_HEIGHT / 2
    player.angle = 0
    player.torch_radius = 20
end sub

sub ant_type.update(ants() as ant_type,mouse_x as long,mouse_y as long,click as long, byref dead as long, byref score as long)
    if squashed = false then
        if click=1 then 
            dim as long dist_x = abs(mouse_x - x)
            dim as long dist_y = abs(mouse_y - y)
            dim as long distance = dist_x * dist_x + dist_y * dist_y

            if distance <= 10 * 10  then squashed=true:score+=1:swap ants(dead), this:dead+=1
        elseif click=2 then 
            dim as long dist_x = abs(mouse_x - x)
            dim as long dist_y = abs(mouse_y - y)
            dim as long distance = dist_x * dist_x + dist_y * dist_y

            if distance <= 10 * 10 *10 *10  then squashed=true:score+=1:swap ants(dead), this:dead+=1
        end if
        x += dx*timval
        y += dy*timval

        ' Bounce off the screen edges
        if x<0 then x=0:dx = -dx
        if x>SCREEN_WIDTH then x=SCREEN_WIDTH:dx = -dx
        if y<0 then y=0:dy = -dy
        if y>SCREEN_HEIGHT then y=SCREEN_HEIGHT:dy = -dy
            
        dim as double dist_x
        dim as double dist_y
        ' Apply simple collision avoidance
        dim as long endj=ubound(ants)
        dim as long active=ubound(ants)-dead
        dim as long max_collision=(5000*5000)/(active)
        
        if endj>max_collision then endj=max_collision
        
        if endj>ubound(ants) then endj=ubound(ants)
        for j as long = dead to endj
            if @this<>@ants(j) then
                dist_x = ants(j).x - x
                dist_y = ants(j).y - y
                if dist_x<10 andalso dist_y<10 then
                    dim as double distance = (dist_x * dist_x + dist_y * dist_y)
                    if distance < 2 * 5 * 2 * 5 then
                        dim as double angle = atan2(dist_y, dist_x)
                        dx -= get_cos(angle)
                        dy -= get_sin(angle)
                            
                        ' Limit the maximum speed
                        dim as double current_speed = dx * dx + dy * dy
                        if current_speed > MAX_SPEED*MAX_SPEED then
                            current_speed = sqr(dx * dx + dy * dy)
                            dx = (dx / current_speed) * MAX_SPEED
                            dy = (dy / current_speed) * MAX_SPEED
                        end if
                        
                        exit for
                    end if
                end if
            end if
        next j

    end if
end sub

sub ant_type.render()
        angle = atan2(dy, dx)
        dim as long size = 5
        dim as long body_color = rgb(20, 10, 10)
        dim as long head_color = rgb(90, 0, 0)
        dim as long squashed_body_color = rgb(60, 40, 30)
        dim as long squashed_head_color = rgb(120, 60, 50)

        ' Calculate leg positions
        dim as double leg_length = size * 1.2
        dim as double leg_angle = angle + 3.14 / 4

        dim as double leg1_x = x + get_cos(leg_angle) * leg_length
        dim as double leg1_y = y + get_sin(leg_angle) * leg_length
        dim as double leg2_x = x + get_cos(leg_angle + 3.14 / 2) * leg_length
        dim as double leg2_y = y + get_sin(leg_angle + 3.14 / 2) * leg_length
        dim as double leg3_x = x + get_cos(leg_angle + 3.14) * leg_length
        dim as double leg3_y = y + get_sin(leg_angle + 3.14) * leg_length
        dim as double leg4_x = x + get_cos(leg_angle + 3 * 3.14 / 2) * leg_length
        dim as double leg4_y = y + get_sin(leg_angle + 3 * 3.14 / 2) * leg_length

        if squashed then body_color=squashed_body_color:head_color=squashed_head_color
        ' Draw ant body
        circle (x, y), size, body_color, , , , f
        ' Draw ant head
        circle (x + get_cos(angle) * (size + 2), y + get_sin(angle) * (size + 2)), size * 0.6, head_color, , , , f
        ' Draw ant legs
        line (leg1_x, leg1_y)-(leg1_x + get_cos(angle) * size, leg1_y + get_sin(angle) * size), body_color
        line (leg2_x, leg2_y)-(leg2_x + get_cos(angle) * size, leg2_y + get_sin(angle) * size), body_color
        line (leg3_x, leg3_y)-(leg3_x + get_cos(angle) * size, leg3_y + get_sin(angle) * size), body_color
        line (leg4_x, leg4_y)-(leg4_x + get_cos(angle) * size, leg4_y + get_sin(angle) * size), body_color

end sub

'=======================================================================
' Main
'=======================================================================

dim as level_type level
level.initialize()

do
    updatefps()
    level.update()
    level.render()
    sleep 1,1
loop until multikey(1)

thebigh
Posts: 43
Joined: Dec 14, 2018 11:11

Re: Marabunta: 2D Ant Game

Post by thebigh »

Nice! Cute little game. I got to to about the 5th level.
Post Reply