¿îÎîîîÎîîîÎÌÌÌÌìîîÎìîîÎÌÌÌÌîîîÎîî"2#""#2"2""##b&2#b&22""#"#2""2#"32#332#332#332#3233#"2#"#""23""3ÎÌÌÌ̈ˆˆ¬ªªª¬ˆˆˆ¬ÌÌ̬ÌÌ̬ÌÀ̬ÌÀÌÌÌìÌÀÀî̬ÀÌ̬ÀÀÀ¬ÀÀÀ¬ÀÀ¬ÌÌ̬ªªª¬ª¬ª¬ªÊ̬ªªªŒˆˆˆÌÀÌÎÌÌÎ̬̪ªÀ쬪ÀÀÀÌîÀìîÌÌîî¬ÌÌ̬ªªª¬ª¬ª¬ªÊ̬ªªªŒˆˆˆÌÀÌÎÌÌÎ̬̪ªÀ쬪ÀÀÀÌîÀìîÌÌîîf``f`ff```f``€ >€88"2#"îÎîîîÎîîîÎîîîÎîî"#2"îÎîîîÎîîîÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìîÌÌìîÌÌìî#b&2îÎìîîÎìîîÎìîîÎìî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîîÌÌîîÌÌîî"2#"îÎîîîÎîîîÎîîîÎîî"2#"îÎîî"#2"îÎîî2""#îÎÌÌ#b&2ÌÌìî#b&2îÎìî2""#îÎÌÌ"#2"ÌÌîî"2#"îÎîî"2#"îÎîîîÎîî"#2"îÎîîîÎîî2""#îÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìî#b&2îÎìîîÎìî2""#îÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîî"2#"îÎîîîÎîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"#2"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìî#b&2îÎìîîÎìîîÎìîîÎìîîÎìîîÎìî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"#2"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìî#b&2îÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"2#"îÎîîîÎîîîÎîîîÎîî"#2"îÎîîîÎîîîÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìîÌÌìîÌÌìî#b&2îÎìîîÎìîîÎìîîÎìî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîîÌÌîîÌÌîî"2#"îÎîîîÎîîîÎîîîÎîî"2#"îÎîîîÎîî"#2"îÎîîîÎîî2""#îÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìî#b&2îÎìîîÎìî2""#îÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîî"2#"îÎîîîÎîî"2#"îÎîîîÎîîîÎîî"#2"îÎîîîÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌ#b&2DDDDDDf`f`DDDDDDÄìîÌÌìîÌÌìî#b&2`f`fîÎìîîÎìîîÎìî2""#`ff`ffîÎÌÌîÎÌÌîÎÌÌ"#2"``ÌÌîîÌÌîîÌÌîî"2#"``îÎîîîÎîîîÎîî"2#"`f`fîÎîî"2#""2#"îÎîîîÎîî"#2"``îÎîî"#2""#2"îÎîîîÎîî2""#``îÎÌÌ2""#2""#îÎÌÌîÎÌÌ#b&2ÌÌìî#b&2#b&2ÌÌìîÌÌìî#b&2îÎìî#b&2#b&2îÎìîîÎìî2""#îÎÌÌ2""#2""#îÎÌÌîÎÌÌ"#2"ÌÌîî"#2""#2"ÌÌîîÌÌîî"2#"îÎîî"2#""2#"îÎîîîÎîî"2#"îÎîî"2#""2#"îÎîîîÎîîîÎîîîÎîî"#2"îÎîî"#2""#2"îÎîîîÎîîîÎîîîÎîî2""#îÎÌÌ2""#2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2ÌÌìî#b&2#b&2ÌÌìîÌÌìîÌÌìîÌÌìî#b&2îÎìî#b&2#b&2îÎìîîÎìîîÎìîîÎìî2""#îÎÌÌ2""#2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2"ÌÌîî"#2""#2"ÌÌîîÌÌîîÌÌîîÌÌîî"2#"îÎîî"2#""2#"îÎîîîÎîîîÎîîîÎîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"2#""2#"îÎîîîÎîî"#2"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"#2""#2"îÎîîîÎîî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ2""#2""#îÎÌÌîÎÌÌ#b&2ÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìî#b&2#b&2ÌÌìîÌÌìî#b&2îÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìî#b&2#b&2îÎìîîÎìî2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ2""#2""#îÎÌÌîÎÌÌ"#2"ÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîî"#2""#2"ÌÌîîÌÌîî"2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"2#""2#"îÎîîîÎîî"2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî"#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîî2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2ÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìîÌÌìî#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2#b&2îÎìîîÎìîîÎìîîÎìîîÎìîîÎìîîÎìî2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#îÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌîÎÌÌ"#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2""#2"ÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîîÌÌîî"2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#""2#"îÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎîîîÎÌÌîÎÌÌÌÌìîÌÌìîîÎìîîÎìîîÎÌÌîÎÌÌÌÌîîÌÌîîîÎîîîÎîîn‘navigable(0,0))) draw_char() draw_attack() draw_front() sprite(117, 40,40,rotation) t=t+1 end a:ÐÒ_÷ÿÐÒ_÷ÿ°ªÊVUU°ªÊVUU˜"-- title: hottic -- author: elsbilf t=0 x=96 y=24 center_x = 70 center_y = 75 length = 20 rotation = 0 attack_frame=-1 MAX_ATTACK_FRAME=30 ATTACK_LENGTH = 5 function radians(degs) return degs*(math.pi/180) end function draw_char() local left_x = center_x + length*math.cos(radians(rotation+180)) local left_y = center_y + length*math.sin(radians(rotation+180)) local right_x = center_x + length*math.cos(radians(rotation)) local right_y = center_y + length*math.sin(radians(rotation)) line(left_x, left_y, right_x, right_y, 4) sprite(117, center_x,center_y,rotation) local left_sprite_x = center_x + 8*math.cos(radians(rotation+180)) local left_sprite_y = center_y + 8*math.sin(radians(rotation+180)) sprite(117,left_sprite_x,left_sprite_y,rotation) end function draw_enemy(x,y,rotation,sprite_id) trace("x = ".. x .. " y = " .. y) sprite(sprite_id, x,y,rotation) local left_sprite_x = x + 8*math.cos(radians(rotation+180)) local left_sprite_y = y + 8*math.sin(radians(rotation+180)) sprite(sprite_id,left_sprite_x,left_sprite_y,rotation) end function move_enemy(positions, enemy) local goal_position = positions[#positions] trace("goal_position" .. goal_position[1] .. " " .. goal_position[2]) local vec_x = goal_position[1]*8 - enemy.position[1] local vec_y = goal_position[2]*8 - enemy.position[2] if vec_x ~= 0 or vec_y ~= 0 then local norm = enemy.speed/math.sqrt(vec_x^2+vec_y^2) enemy.position[1] = enemy.position[1] + vec_x*norm enemy.position[2] = enemy.position[2] + vec_y*norm end local dist = math.sqrt((goal_position[1]*8-enemy.position[1])^2 + (goal_position[2]*8 - enemy.position[2])^2) trace("dist = " .. dist) if dist < 4 then table.remove(positions,#positions) end end function start_attack() if attack_frame<0 then attack_frame=0 end end function draw_attack() if attack_frame>=0 then attack_x = center_x + ATTACK_LENGTH*math.cos(radians(rotation-attack_frame/MAX_ATTACK_FRAME*180)) attack_y = center_y + ATTACK_LENGTH*math.sin(radians(rotation-attack_frame/MAX_ATTACK_FRAME*180)) line(center_x,center_y, attack_x, attack_y,5) attack_frame=attack_frame +1 if attack_frame>=MAX_ATTACK_FRAME then attack_frame=-1 end end end function draw_front() right_x = center_x + length*math.cos(radians(rotation-90)) right_y = center_y + length*math.sin(radians(rotation-90)) line(center_x, center_y, right_x, right_y, 6) end function move(forward) center_x = center_x + forward*math.cos(radians(rotation+90)) center_y = center_y + forward*math.sin(radians(rotation+90)) end function sprite(id, x, y, rot) local rad = radians(rot) local cos, sin = math.cos(rad), math.sin(rad) local start = 0x4000 + id * 8*8/2 -- 8*8/2 bytes per 4bpp sprite -- bounding box: rotate the 4 corners forward to find how far -- the sprite spreads out on screen, so we only scan what's needed local corners = {{0,0},{7,0},{0,7},{7,7}} local minX, maxX, minY, maxY = 0, 0, 0, 0 for _,c in ipairs(corners) do local cxr = c[1]*cos - c[2]*sin local cyr = c[1]*sin + c[2]*cos minX, maxX = math.min(minX, cxr), math.max(maxX, cxr) minY, maxY = math.min(minY, cyr), math.max(maxY, cyr) end for dy = math.floor(minY), math.ceil(maxY) do for dx = math.floor(minX), math.ceil(maxX) do -- inverse-rotate the destination offset back into sprite space local col = dx*cos + dy*sin local row = -dx*sin + dy*cos -- nearest-neighbor sample local fx = math.floor(col + 0.5) local fy = math.floor(row + 0.5) if fx >= 0 and fx < 8 and fy >= 0 and fy < 8 then local i = fy*8 + fx local nibble = start*2 + i local c = peek4(nibble) pix(x + dx, y + dy, c) end end end end heap = {} heap.__index = heap function heap:push(element, value) table.insert(self.array, {element, value}) if #self.array > 1 then local current_index = #self.array while current_index > 1 and self.array[current_index//2][2] > value do local parent = current_index//2 local tmp = self.array[current_index] self.array[current_index] = self.array[parent] self.array[parent] = tmp current_index = parent end end end function heap:pop() if #self.array == 0 then return nil end local ret = self.array[1] self.array[1] = self.array[#self.array] table.remove(self.array) local current = 1 while true do local left = current*2 local right = left+1 local smallest = current if left <= #self.array and self.array[left][2] < self.array[smallest][2] then smallest = left end if right <= #self.array and self.array[right][2] < self.array[smallest][2] then smallest = right end if smallest == current then break end self.array[current], self.array[smallest] = self.array[smallest], self.array[current] current = smallest end return ret end function heap:isEmpty() return #self.array == 0 end function heap:new() local obj = {} setmetatable(obj, heap) obj.array = {} return obj end function A_star(x_start, y_start, x_end, y_end, invalid_pos) local path = {} local final = nil local explored = {} local ps = heap:new() ps:push({x_start, y_start, 0, nil}, math.sqrt((x_start-x_end)^2 + (y_start-y_end)^2)) while not ps:isEmpty() do local popped = ps:pop() local node = popped[1] -- {x,y,cost,parent} local key = node[1] .. "," .. node[2] if explored[key] then goto continue end explored[key] = true if node[1] == x_end and node[2] == y_end then final = node break end for i=-1,1 do for j=-1,1 do if not (i==0 and j==0) then local nx, ny = node[1]+i, node[2]+j local nkey = nx .. "," .. ny if not invalid_pos[nkey] and not explored[nkey] then local new_node = {nx, ny, node[3]+math.sqrt(i^2+j^2), node} -- parent = node, not popped ps:push(new_node, math.sqrt((nx-x_end)^2+(ny-y_end)^2) + new_node[3]) end end end end ::continue:: end while final ~= nil do table.insert(path, 1, {final[1], final[2]}) -- final is a node now, so final[1]/final[2] are x/y final = final[4] end return path end function table_to_string(t) local s = "{" for k,v in pairs(t) do s = s .. "{" .. v[1] .. ", " .. v[2] .. "}" .. ", " end return s .. "}" end function table_to_string2(t) local s = "{" for v,k in pairs(t) do s = s .. "{" .. v .. ", " .. "}" .. ", " end return s .. "}" end function not_navigable(x,y) local occupied = {} for row=y,y+16 do for col=x,x+29 do if mget(col,row)==0 then occupied[col .. "," .. row] = true end end end return occupied end function draw_trajectory(tiles) --trace("length_tiles " .. #tiles) for _,coord in pairs(tiles) do spr(1,coord[1]*8,coord[2]*8) end end function check_collision(x,y) return fget(mget(x//8,y//8),7) end function try_move(forward,rot_vec) local tried_rotation = rotation + rot_vec local tried_x = center_x + forward*math.cos(radians(rotation+90)) local tried_y = center_y + forward*math.sin(radians(rotation+90)) if not check_borders(tried_x, tried_y, tried_rotation) then --trace("move") center_x = tried_x center_y = tried_y rotation = tried_rotation end end function check_borders(cx, cy, rot) local half_w = 8 -- half of the 16px width (left-right), still symmetric local depth = 8 -- full depth, extends backward only local wx, wy = math.cos(radians(rot)), math.sin(radians(rot)) -- width axis local dx, dy = math.cos(radians(rot-90)), math.sin(radians(rot-90)) -- backward axis -- front edge sits exactly on (cx,cy) local fl_x, fl_y = cx - half_w*wx, cy - half_w*wy local fr_x, fr_y = cx + half_w*wx, cy + half_w*wy -- back edge is depth (8px) behind the front edge local bl_x, bl_y = fl_x - depth*dx, fl_y - depth*dy local br_x, br_y = fr_x - depth*dx, fr_y - depth*dy -- scan the two width edges (16px) for i = 0,16 do local t = i/16 local fx, fy = fl_x + (fr_x-fl_x)*t, fl_y + (fr_y-fl_y)*t local bx, by = bl_x + (br_x-bl_x)*t, bl_y + (br_y-bl_y)*t if check_collision(fx,fy) or check_collision(bx,by) then return true end end -- scan the two depth edges (8px) for i = 0,8 do local t = i/8 local lx, ly = fl_x + (bl_x-fl_x)*t, fl_y + (bl_y-fl_y)*t local rx, ry = fr_x + (br_x-fr_x)*t, fr_y + (br_y-fr_y)*t if check_collision(lx,ly) or check_collision(rx,ry) then return true end end return false end function TIC() if btn(0) then y=y-1;try_move(-1,0) end if btn(1) then y=y+1;try_move(1,0) end if btn(2) then x=x-1;try_move(0,-5) end if btn(3) then x=x+1;try_move(0,5) end if btnp(4) then trace("attack");start_attack() end cls(13) map() if t==0 then traj = A_star(1,2,20,10, not_navigable(0,0)) enemy = {speed= 0.5, position={20*8, 10*8}} end draw_trajectory(traj) draw_char() draw_attack() draw_front() draw_enemy(enemy.position[1], enemy.position[2], 45, 2) move_enemy(traj, enemy) t=t+1 end