mirror of
https://github.com/creyD/ludum_dare_46.git
synced 2026-06-12 05:22:23 +02:00
372 lines
9.4 KiB
GDScript
372 lines
9.4 KiB
GDScript
extends KinematicBody2D
|
|
|
|
class_name Hero
|
|
|
|
const PrioQueue = preload("prio_queue.gd") # Relative path
|
|
const Grid = preload("res://Maps/Grid.gd")
|
|
|
|
var grid
|
|
|
|
enum{
|
|
STEP,
|
|
ROLL,
|
|
ATTAC,
|
|
NOTHING
|
|
}
|
|
|
|
enum{
|
|
EXECUTING,
|
|
AI_MOVE
|
|
}
|
|
|
|
var ExecutionState = AI_MOVE
|
|
var ai_movement_state = NOTHING
|
|
|
|
var numbers = [0,0,0,0,0,0,0,0,0,0]
|
|
var prios = [7,0,6,5,4,3,2,1,1,4]
|
|
|
|
var targetFieldCur = [0,0]
|
|
var targetFieldUsed = false
|
|
|
|
var actionKind = Grid.Kind.TERMINAL_SYMBOL
|
|
var actionFieldUsed = false
|
|
|
|
var areaRefList = []
|
|
|
|
var threadTime = 0.4
|
|
var threadDelta = 0.0
|
|
|
|
var hitDelta = 0.0
|
|
var hitTreshhold = 0.1
|
|
|
|
var aiDelta = 0.0
|
|
var aiTreshhold = 0.4
|
|
|
|
#calculates the sum of all present prios
|
|
func calcTotalPrio():
|
|
var sum = 0
|
|
var i = Grid.Kind.BOSS
|
|
while i != Grid.Kind.TERMINAL_SYMBOL:
|
|
if(numbers[i]>0):
|
|
sum += prios[i]
|
|
i += 1
|
|
return sum
|
|
|
|
#calculates the relative porio
|
|
func calcRelPrio(index, sum) -> float:
|
|
if(sum==0):
|
|
return 0.0
|
|
return float(prios[index])/float(sum)
|
|
|
|
#calucaltes the prio table of all enemies[0,1)
|
|
func calcPrioTable():
|
|
var table = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]
|
|
numbers = grid.countTargets(numbers)
|
|
var lower = 0.0
|
|
var sum = calcTotalPrio()
|
|
|
|
var i = 0;
|
|
while i != Grid.Kind.TERMINAL_SYMBOL:
|
|
if(numbers[i]!=0):
|
|
lower += calcRelPrio(i, sum)
|
|
table[i] = lower
|
|
i += 1
|
|
|
|
return table
|
|
#14+7 0.999
|
|
#updates heart and bonfire prio
|
|
func adjustPrio(currentHealth, maxHealth):
|
|
var prioVal = 20.0 - (float(currentHealth)/float(maxHealth))*20.0
|
|
var bonfire = prioVal + 1
|
|
var hearts = prioVal
|
|
if(hearts < 0):
|
|
hearts = 0
|
|
prios[Grid.Kind.BONFIRE]=bonfire
|
|
prios[Grid.Kind.HEART]=hearts
|
|
|
|
#return the enemie which will be attacked
|
|
func calcEnemyKind():
|
|
var table = calcPrioTable()
|
|
var number = randf()
|
|
var i = 0
|
|
while i!=Grid.Kind.TERMINAL_SYMBOL and table[i] <= number:
|
|
i += 1
|
|
return i
|
|
|
|
#returns a move
|
|
func getMoveDescription(myPosition : Vector2, targetPositions):
|
|
return AStar(myPosition, targetPositions)
|
|
|
|
|
|
func getCost(field):
|
|
var cost = 0
|
|
for i in grid.object_grid[field.x][field.y]:
|
|
match i:
|
|
Grid.Kind.DAMAGE:
|
|
cost += 15
|
|
Grid.Kind.SLOW:
|
|
cost += 4
|
|
return cost
|
|
|
|
#return an heurestic of distance
|
|
# curr - current position
|
|
# targ - a target position
|
|
func h_fn(curr, target):
|
|
return 0
|
|
var h = min(target[0]-curr[0],target[1]-curr[1])
|
|
return h
|
|
|
|
# currCost - currentCost
|
|
# target - position of the field to move to
|
|
func g_fn(currCost, target):
|
|
return currCost + getCost(target)
|
|
|
|
# Returns the list of adjacent nodes
|
|
func adjacent(currentPosition, can_roll = true):
|
|
var adj := []
|
|
#adj.append([STEP, Vector2(0,0)])
|
|
var p = currentPosition
|
|
var pot_adj_step = [[p[0]-1, p[1]-1], [p[0]-0, p[1]-1], [p[0]+1, p[1]-1],
|
|
[p[0]-1, p[1]-0], [p[0]+1, p[1]+0],
|
|
[p[0]-1, p[1]+1], [p[0]+0, p[1]+1], [p[0]+1, p[1]+1]]
|
|
|
|
var pot_adj_roll = [[p[0]-0, p[1]-2],
|
|
[p[0]-2, p[1]-0],[p[0]+2, p[1]+0],
|
|
[p[0]+0, p[1]+2]]
|
|
|
|
|
|
for i in range(pot_adj_step.size()):
|
|
var next = pot_adj_step[i]
|
|
if(next[0]<0):
|
|
continue
|
|
if(next[0]>13):
|
|
continue
|
|
if(next[1]<0):
|
|
continue
|
|
if(next[1]>6):
|
|
continue
|
|
if(grid.used_grid[next[0]][next[1]]):
|
|
continue
|
|
if(grid._is_in_grid(Vector2(next[0], next[1])) ==false):
|
|
continue
|
|
if(grid.object_grid[next[0]][next[1]][0]!=Grid.Kind.WALL):
|
|
if(i==0):
|
|
continue
|
|
if(grid.object_grid[pot_adj_step[1][0]][pot_adj_step[1][1]][0]!=Grid.Kind.WALL &&
|
|
grid.object_grid[pot_adj_step[3][0]][pot_adj_step[3][1]][0]!=Grid.Kind.WALL):
|
|
adj.append([STEP, Vector2(next[0],next[1]),1.1])
|
|
continue
|
|
if(i==2):
|
|
continue
|
|
if(grid.object_grid[pot_adj_step[1][0]][pot_adj_step[1][1]][0]!=Grid.Kind.WALL &&
|
|
grid.object_grid[pot_adj_step[4][0]][pot_adj_step[4][1]][0]!=Grid.Kind.WALL):
|
|
adj.append([STEP, Vector2(next[0],next[1]),1.1])
|
|
continue
|
|
if(i==5):
|
|
continue
|
|
if(grid.object_grid[pot_adj_step[3][0]][pot_adj_step[3][1]][0]!=Grid.Kind.WALL &&
|
|
grid.object_grid[pot_adj_step[6][0]][pot_adj_step[6][1]][0]!=Grid.Kind.WALL):
|
|
adj.append([STEP, Vector2(next[0],next[1]),1.1])
|
|
continue
|
|
if(i==7):
|
|
continue
|
|
if(grid.object_grid[pot_adj_step[4][0]][pot_adj_step[4][1]][0]!=Grid.Kind.WALL &&
|
|
grid.object_grid[pot_adj_step[6][0]][pot_adj_step[6][1]][0]!=Grid.Kind.WALL):
|
|
adj.append([STEP, Vector2(next[0],next[1]),1.1])
|
|
continue
|
|
|
|
adj.append([STEP, Vector2(next[0],next[1]),1.0])
|
|
|
|
|
|
for i in range(pot_adj_roll.size()):
|
|
var next = pot_adj_roll[i]
|
|
if(next[0]<0):
|
|
continue
|
|
if(next[0]>13):
|
|
continue
|
|
if(next[1]<0):
|
|
continue
|
|
if(next[1]>6):
|
|
continue
|
|
if(grid.used_grid[next[0]][next[1]]):
|
|
continue
|
|
if(grid._is_in_grid(Vector2(next[0], next[1])) == false):
|
|
continue
|
|
if(grid.object_grid[next[0]][next[1]][0]!=Grid.Kind.WALL):
|
|
if(i==0):
|
|
if(grid.object_grid[next[0]+0][next[1]+1][0]!=Grid.Kind.WALL):
|
|
adj.append([ROLL, Vector2(next[0],next[1]),1.0])
|
|
if(i==1):
|
|
if(grid.object_grid[next[0]+1][next[1]+0][0]!=Grid.Kind.WALL):
|
|
adj.append([ROLL, Vector2(next[0],next[1]),1.0])
|
|
if(i==2):
|
|
if(grid.object_grid[next[0]-1][next[1]+0][0]!=Grid.Kind.WALL):
|
|
adj.append([ROLL, Vector2(next[0],next[1]),1.0])
|
|
if(i==3):
|
|
if(grid.object_grid[next[0]+0][next[1]-1][0]!=Grid.Kind.WALL):
|
|
adj.append([ROLL, Vector2(next[0],next[1]),1.0])
|
|
|
|
return adj
|
|
|
|
|
|
func AStar(source, target):
|
|
#swap rtarget and source, when target source istr reached, do inversxse step
|
|
# node layout [g+h(x), g(x), current, from, kind]
|
|
var tmp = source
|
|
source = target
|
|
target = tmp
|
|
|
|
var Q = PrioQueue.new()
|
|
Q.insert([0,0, [source[0], source[1]], [source[0], source[1]], NOTHING] )
|
|
while !Q.empty():
|
|
var node = Q.delMin()
|
|
|
|
# Check if reached
|
|
if(node[2][0] == target[0] and node[2][1] == target[1]):
|
|
return [node[4], node[3]] # 4 is kind | 3 is from
|
|
|
|
# Set flag
|
|
grid.used_grid[node[2][0]][node[2][1]] = true
|
|
var adj_list = adjacent(node[2])
|
|
var current_field = node[2]
|
|
for i in adj_list:
|
|
var move_cost = i[2]
|
|
|
|
var g_val = g_fn(node[1]+move_cost, i[1])
|
|
var h_val = h_fn(i[1], target)
|
|
|
|
#[g+h(x), g(x), current, from, kind]
|
|
var new_node = [g_val+h_val, g_val,i[1], node[2], i[0]]
|
|
Q.insert(new_node)
|
|
|
|
return [NOTHING, [0,0]]
|
|
|
|
|
|
func movement_calulcaotr():
|
|
var currentPosition = grid._pixel_to_grid_coords(global_position)
|
|
|
|
var enemyKind
|
|
|
|
numbers = grid.countTargets(numbers)
|
|
if(actionKind == grid.Kind.TERMINAL_SYMBOL || numbers[actionKind]==0 || actionFieldUsed==false):
|
|
enemyKind = calcEnemyKind()
|
|
actionKind = enemyKind
|
|
actionFieldUsed = true
|
|
else:
|
|
enemyKind = actionKind
|
|
|
|
if(enemyKind==Grid.Kind.TERMINAL_SYMBOL):
|
|
return
|
|
|
|
var targetField = grid.get_nearest(currentPosition, enemyKind)
|
|
if(targetField==[-1,-1]):
|
|
return
|
|
|
|
|
|
var MoveAdvice = getMoveDescription(currentPosition, targetField)
|
|
grid.reset_history()
|
|
|
|
return MoveAdvice
|
|
|
|
func is_hittable():
|
|
var length = areaRefList.size()
|
|
if length == 0:
|
|
return null
|
|
return instance_from_id(areaRefList[0]).global_position
|
|
|
|
func hit_or_miss(target, current, delta):
|
|
attac(Vector2(target[0]-current[0], target[1]-current[1]), delta*4)
|
|
|
|
func movement_decider_ai(target, kindOfStep, delta):
|
|
var currentPosition = grid._pixel_to_grid_coords(global_position)
|
|
|
|
if hitDelta >= hitTreshhold && randf() <0.5:
|
|
hitDelta -= hitTreshhold
|
|
var currentPixel = global_position
|
|
var hitPixelTarget = is_hittable()
|
|
|
|
if hitPixelTarget!=null:
|
|
hit_or_miss(hitPixelTarget, currentPixel, delta*4)
|
|
return
|
|
|
|
else:
|
|
if(kindOfStep==STEP):
|
|
run(Vector2(target[0]-currentPosition[0], target[1]-currentPosition[1]), delta*4)
|
|
targetFieldCur = target
|
|
targetFieldUsed = true
|
|
ai_movement_state = STEP
|
|
elif(kindOfStep==ROLL):
|
|
roll(Vector2(target[0]-currentPosition[0], target[1]-currentPosition[1]), delta*4)
|
|
targetFieldCur = target
|
|
targetFieldUsed = true
|
|
ExecutionState = EXECUTING
|
|
|
|
|
|
|
|
func movement_execution(delta):
|
|
|
|
if hitDelta >= hitTreshhold && randf() <0.5:
|
|
hitDelta -= hitTreshhold
|
|
var currentPixel = global_position
|
|
var hitPixelTarget = is_hittable()
|
|
|
|
if hitPixelTarget!=null:
|
|
hit_or_miss(hitPixelTarget, currentPixel, delta*4)
|
|
return
|
|
|
|
if(targetFieldUsed):
|
|
var cur = grid._pixel_to_grid_coords(global_position)
|
|
var distance = sqrt(pow(cur[0]-targetFieldCur[0],2)+ pow(cur[1]-targetFieldCur[1],2))
|
|
if(distance<0.1):
|
|
targetFieldUsed = false
|
|
ExecutionState = AI_MOVE
|
|
var actionField = grid.get_nearest(cur, actionKind)
|
|
if(targetFieldCur[0]==actionField[0]&&targetFieldCur[1]==actionField[1]):
|
|
actionFieldUsed = false
|
|
else:
|
|
var currentPosition = grid._pixel_to_grid_coords(global_position)
|
|
if(ai_movement_state==STEP):
|
|
run(Vector2(targetFieldCur[0]-currentPosition[0], targetFieldCur[1]-currentPosition[1]), delta*4)
|
|
elif(ai_movement_state==ROLL):
|
|
run(Vector2(targetFieldCur[0]-currentPosition[0], targetFieldCur[1]-currentPosition[1]), delta*4)
|
|
|
|
|
|
func reset_exeution_state(delta):
|
|
threadDelta = threadDelta + delta
|
|
if(threadDelta>threadTime):
|
|
ExecutionState = AI_MOVE
|
|
actionFieldUsed = false
|
|
|
|
|
|
|
|
func makeMove(delta):
|
|
hitDelta += delta
|
|
aiDelta+=delta
|
|
if(aiDelta>aiTreshhold):
|
|
if ExecutionState == AI_MOVE:
|
|
threadDelta = 0
|
|
var MoveAdvice = movement_calulcaotr()
|
|
if(MoveAdvice==null):
|
|
return
|
|
var target = MoveAdvice[1]
|
|
movement_decider_ai(target, MoveAdvice[0], delta)
|
|
|
|
if ExecutionState == EXECUTING:
|
|
movement_execution(delta)
|
|
reset_exeution_state(delta)
|
|
|
|
|
|
# API Interface for ai_hero -> methods are handled in player.gd
|
|
func attac(direction, delta):
|
|
pass
|
|
|
|
|
|
func roll(direction, delta):
|
|
pass
|
|
|
|
|
|
func run(direction, delta):
|
|
pass
|
|
|
|
#todo
|