?-
  G_Zoom:=2.5,
  G_PosX:= -100.0,
  G_PosY:= -60.0,
  G_PlayWith:=0,
  G_Action:=no,
  G_MaxObj:=128,
  array(cc_x,G_MaxObj,0.0),
  array(cc_y,G_MaxObj,0.0),
  array(cc_t,G_MaxObj,0),
  array(cc_h,G_MaxObj,100),
  array(attack,G_MaxObj,1),
  array(status,G_MaxObj,8),  % 0-stand by , 1-fire stand by , 2-attack, 3-fire, 4-move, 6-dead
  array(tar_x,G_MaxObj,0.0),
  array(tar_y,G_MaxObj,0.0),
  array(born,G_MaxObj,0),
  G_CC:=2,
  cc_x(1):=random(2000)-1000,
  cc_y(1):=random(2000)-1000,
  cc_t(1):=1,
  pen(0,0),
  window(G_SelWin,_,sel_func(_),"Selected",810,10,200,600),
  window(G_MainWin,_,main_func(_),"War Craft",10,10,800,600).

selection().

sel_func(paint):-
  selection(|List),
  draw_list(List,0).

sel_func(close):-
    close(G_MainWin),
    fail.

menu_move(press):- G_Action:= move, set_text("War Craft (show me where to move)", G_MainWin).
menu_attack(press):- G_Action:= attack, set_text("War Craft (show me who to attack)", G_MainWin).
menu_stop(press):- action(stop).
menu_build(press):- action(build).

menu_play_with(init):-
  menu(checked,_,_,play_with_red(_),"The &Red"),
  menu(normal,_,_,play_with_blue(_),"The &Blue"),
  menu(normal,_,_,play_with_both(_),"&Both").

play_with_red(press):- play_with(0).
play_with_blue(press):- play_with(1).
play_with_both(press):- play_with(2).

play_with(PlayWith):-
  modify_menu(G_Menu,G_PlayWith+1,normal,_),
  G_PlayWith :=PlayWith,
  modify_menu(G_Menu,PlayWith+1,checked,_).

menu_help(press):-
  message("War Craft - Help",
  "You are in an infinite forest. You are involved in one epic battle.

In order to move use the arrows. Zoom in and out by the mouse wheel (in combination with the left mouse button). Select objects by the mouse.",i).
  
main_func(init):-
  menu(normal, _, _, menu_move(_), "&Move"),
  menu(normal, _, _, menu_attack(_), "&Attack"),
  menu(normal, _, _, menu_stop(_), "&Stop"),
  menu(normal, _, _, menu_build(_), "&Build"),
  menu(pop_up, G_Menu, _, menu_play_with(_),"&Play with"),
  menu(right, _, _, menu_help(_), "&Help"),
  _ is set_timer(_, 0.2, time_func).

main_func(paint):-
  G_Zoom>0.005,
  paint_rect(A,B,C,D),
  X1 is floor((G_PosX+A/G_Zoom - 50)/100),
  X2 is floor((G_PosX+C/G_Zoom)/100),
  Y1 is floor((G_PosY+B/G_Zoom - 50)/100),
  Y2 is floor((G_PosY+D/G_Zoom)/100),
  for(J,Y1,Y2),
    for(I,X1,X2),
      X is floor((I*100-G_PosX)*G_Zoom),
      Y is floor((J*100-G_PosY)*G_Zoom),
      Size is floor(175*G_Zoom),
      paint_rect(X,Y,X+Size,Y+Size),
      my_start_random_from(I, J),
      (is_it_wet->
        Elx is  1+ceiling(100*G_Zoom),
        Ely is  1+ceiling(100*G_Zoom),
        Random1 is  1+ceiling((10+random(90))*G_Zoom),
        %brush(rgb(255,0,0)), rect(X, Y, X+Elx, Y+Ely),
        brush(rgb(0,255,255)),
        my_start_random_from(I, J-1),
        (is_it_wet->
          my_start_random_from(I, J+1),
          (is_it_wet->
            rect(X, Y, X+Elx, Y+Ely)
          else
            my_start_random_from(I-1, J),
            Left is (is_it_wet-> 1 else 0),
            my_start_random_from(I+1, J),
            Right is (is_it_wet-> 1 else 0),
            (Left+Right=:=2 ->
              rect(X, Y, X+Elx, Y+Ely)
            else (Left+Right=:=0 ->
              draw_chord(X, Y-Random1, X+Elx, Y+Random1, X, Y, X+Elx, Y)
            else (Left=:=1 ->
              draw_pie(X-Elx, Y-Ely, X+Elx, Y+Ely, X, Y+Ely, X+Elx, Y)
            else
              draw_pie(X, Y-Ely, X+2*Elx, Y+Ely, X, Y, X+Elx, Y+Ely)
            )))
          )
        else
          my_start_random_from(I-1, J),
          Left is (is_it_wet-> 1 else 0),
          my_start_random_from(I+1, J),
          Right is (is_it_wet-> 1 else 0),
          my_start_random_from(I, J+1),
          Down is (is_it_wet-> 1 else 0),
          (Left+Right=:=2 ->
            rect(X, Y, X+Elx, Y+Ely)
          else (Left+Right=:=0 ->
            (Down=:=1 ->
              draw_chord(X, Y+Ely-Random1, X+Elx, Y+Ely+Random1, X+Elx, Y+Ely, X, Y+Ely)
            else
              fail, ellipse(X, Y, X+Elx, Y+Ely)
            )
          else (Left=:=1 ->
            (Down=:=1 ->
              draw_pie(X-Elx, Y, X+Elx, Y+2*Ely, X+Elx, Y+Ely, X, Y)
            else
              draw_chord(X-Random1, Y, X+Random1, Y+Ely, X, Y+Ely, X, Y)
            )
          else
            (Down=:=1 ->
              draw_pie(X, Y, X+2*Elx, Y+2*Ely, X+Elx, Y, X, Y+Ely)
            else
              draw_chord(X+Elx-Random1, Y, X+Elx+Random1, Y+Ely, X+Elx, Y, X+Elx, Y+Ely)
            )
          )))
        ),
        fail
      ),
      G_Zoom>0.05,
      for(_, 0,10*random(2)),
        Elx is  X+floor(random(100)*G_Zoom),
        Ely is  Y+floor(random(100)*G_Zoom),
        Elr is  floor((5 +random(10))*G_Zoom),
        brush(rgb(0,random(256),0)),
        fill_polygon(Elx+Elr, Ely, Elx, Ely+3*Elr, Elx+2*Elr, Ely+3*Elr),
  fail.

main_func(paint):-
  G_Zoom>0.05,
  for(I,0,G_CC-1),
     draw_object(I),
  fail.

main_func(key_down(77, Rep)):- menu_move(press).
main_func(key_down(65, Rep)):- menu_attack(press).
main_func(key_down(66, Rep)):- menu_build(press).
main_func(key_down(83, Rep)):- menu_stop(press).

main_func(key_down(Char, Rep)):-
  Xold:=G_PosX,
  Yold:=G_PosY,
  (Char=37; Char=36; Char=35-> % left
    G_PosX:= G_PosX - G_Xsize*0.02*Rep/G_Zoom
  ),
  (Char=39; Char=34; Char=33-> % right
    G_PosX:= G_PosX + G_Xsize*0.02*Rep/G_Zoom
  ),
  (Char=38; Char=36; Char=33-> % up
    G_PosY:= G_PosY - G_Ysize*0.02*Rep/G_Zoom
  ),
  (Char=40; Char=34; Char=35-> % down
    G_PosY:= G_PosY + G_Ysize*0.02*Rep/G_Zoom
  ),
  move(Xold, Yold).

main_func(size(X,Y)):-
  G_Xsize:=X,
  G_Ysize:=Y.

main_func(mouse_wheel(Delta, X, Y)):-
  (message_flags(left)->
    client(G_MainWin,A,B,_,_),
    zoom(0-Delta, X-A, Y-B)
  else
    Yold:=G_PosY,
    G_PosY:= G_PosY - 3*G_Ysize*0.02*Delta/G_Zoom,
    move(G_PosX, Yold)
  ).

main_func(mouse_click(X,Y)):-
  G_ClickX:=G_PosX+X/G_Zoom,
  G_ClickY:=G_PosY+Y/G_Zoom.

main_func(mouse_click_up(X,Y)):-
  A:=G_PosX+X/G_Zoom,
  B:=G_PosY+Y/G_Zoom,
  (G_Action \= no->
    set_text("War Craft", G_MainWin),
    action(G_Action, A, B)
  else ((A-G_ClickX)**2+(B-G_ClickY)**2 > 9*G_Zoom**2 ->
    MinX is min(G_ClickX,A),
    MaxX is max(G_ClickX,A),
    MinY is min(G_ClickY,B),
    MaxY is max(G_ClickY,B),
    findall(Obj,find_in_rect(MinX,MinY,MaxX,MaxY,Obj),List),
    make_new_sel(List)
  else (find_object(A,B,Obj,0) ->
    make_new_sel([Obj])
  else
    make_new_sel([])
  ))),
  update_window(G_SelWin).

main_func(close):-
    close(G_SelWin),
    fail.

my_start_random_from(I, J):-
      Abs is abs(I) + abs(J),
      start_random_from(true_value(I>0)+ 2*true_value(J>0) 
       + 4* ((1+Abs)*Abs//2+abs(I))).

is_it_wet:-
  random(100)<30.

make_new_sel(List):-
  selection(|OldList),
  set(selection(|List)),
  union(OldList, List, Union),
  draw_all(Union).

draw_all([]).
draw_all([H|T]):-
  draw_object(H),
  draw_all(T).

union([], B, B).
union([H|T], B, [H|Res]):-
  union(T, B, Res).

move(Xold, Yold):-
  scroll_window(G_MainWin, ceiling(Xold*G_Zoom)-ceiling(G_PosX*G_Zoom), ceiling(Yold*G_Zoom)-ceiling(G_PosY*G_Zoom), 1).

zoom(Delta, X, Y):-
  G_Zoom:=G_Zoom*0.9**Delta,
  G_PosX:= G_PosX - X* (1 - 0.9**Delta)/G_Zoom,
  G_PosY:= G_PosY - Y* (1 - 0.9**Delta)/G_Zoom,
  update_window(G_MainWin).

action(move, A, B):-
  G_Action:=no,
  selection(|List),
  element(P, List),
  cc_t(P) >= 100,
  cc_t(P) mod 4 =\= 1-G_PlayWith,
  tar_x(P):=A,
  tar_y(P):=B,
  status(P):=4,
  fail.

action(attack, A, B):-
  G_Action:=no,
  find_object(A,B,Obj,0),
  selection(|List),
  element(P, List),
  cc_t(P) >= 100,
  cc_t(P) mod 4 =\= 1-G_PlayWith,
  attack(P):=Obj,
  status(P):=2,
  fail.

action(stop):-
  selection(|List),
  element(P, List),
  cc_t(P) >= 100,
  cc_t(P) mod 4 =\= 1-G_PlayWith,
  status(P):=status(P) mod 2,
  fail.

action(build):-
  selection(|List),
  element(P, List),
  cc_t(P) >= 100,
  cc_t(P) mod 4 =\= 1-G_PlayWith,
  build(P),
  draw_object(P),
  fail.

time_func(end):-
  (G_Action \= no ->
    cursor(c)
  ),
  update_window(G_SelWin),
  for(P,0,G_CC-1),
    (status(P) =:= 4 ->
      move_object(P,tar_x(P),tar_y(P),2)
    else ( status(P) mod 2 =:= 1 ->
      move_shell(P,cc_x(attack(P)),cc_y(attack(P)),4)
    else ( status(P) =:= 2 ->
      ( cc_t(attack(P)) mod 4 =:= 2 ->
        status(P):=0
      else ( (cc_x(P)-cc_x(attack(P)))**2 + (cc_y(P)-cc_y(attack(P)))**2  > 10000 ->
        move_object(P,cc_x(attack(P)),cc_y(attack(P)),3)
      else 
        status(P):=3,
        tar_x(P):=cc_x(P),
        tar_y(P):=cc_y(P)
      ))
    else ( status(P) =:= 0 ->
      (find_enemy(P,near,Enemy) -> 
        status(P):=1,
        attack(P):=Enemy,
        tar_x(P):=cc_x(P),
        tar_y(P):=cc_y(P)
      )
    else ( status(P) =:= 8 ->
      born(P):=born(P)+5,
      (born(P)>100 ->
        born(P):=0,
        make_soldier(P)
      )
    ))))),      
    fail.

make_soldier(P):-
  find_place(A, B, 0, 20, P, 5),
  find_new_object(New), !,
  cc_x(New):=A,
  cc_y(New):=B,
  cc_t(New):=cc_t(P)+100,
  cc_h(New):=100,
  (cc_t(New) mod 4 =:= G_PlayWith->
    status(New):=0
  else (random(100) < 80, find_enemy(P,far,Enemy) -> 
    status(New):=2,
    attack(New):=Enemy
  else
    status(New):=4,
    tar_x(New):=cc_x(New)+(200+random(100))*(1-2*random(2)),
    tar_y(New):=cc_y(New)+(200+random(100))*(1-2*random(2))
  )),
  draw_object(New).

move_object(P,X,Y,Speed):-
  Length is sqrt( (cc_x(P)-X)**2 + (cc_y(P)-Y)**2 ),
  (Length < Speed ->
    A is X-cc_x(P),
    B is Y-cc_y(P)
  else
    A is Speed*(X-cc_x(P))/Length,
    B is Speed*(Y-cc_y(P))/Length
  ),
  find_place(X2, Y2, A, B, P, 5),
  clean_object(P),
  cc_x(P):=X2,
  cc_y(P):=Y2,
  (Length < Speed ->
    (cc_t(P) mod 4 =:= G_PlayWith->
      status(P):=0
    else
      build(P)
    )
  ),
  draw_object(P).

build(P):-
  (find_another_object(cc_x(P), cc_y(P), P, 12)->
    status(P):=0
  else
    status(P):=8,
    cc_t(P):=cc_t(P)-100
  ).

move_shell(P,X,Y,Speed):-
  draw_shell(P,1),
  Length is sqrt( (tar_x(P)-X)**2 + (tar_y(P)-Y)**2 ),
  (Length < Speed ->
    tar_x(P):=X,
    tar_y(P):=Y,
    status(P):=status(P)-1,
    kill_object(P, 5)
  else
    tar_x(P):=tar_x(P)+Speed*(X-tar_x(P))/Length,
    tar_y(P):=tar_y(P)+Speed*(Y-tar_y(P))/Length
  ),
  draw_shell(P,0).


kill_object(P,Damage):-
  P1 is attack(P),
  cc_h(P1):=cc_h(P1)-Damage,
  (cc_h(P1) =< 0 ->
    clean_object(P1),
    cc_t(P1):=2,
    status(P):=0,
    status(P1):=6
  ).    

element(A,[A|_]).
element(A,[_|T]):-element(A,T).

draw_object(I):-
  selection(|S),
  Selected is (element(I,S) -> 150 else 0),
  (cc_t(I) mod 4 =:= 0 ->
    brush(rgb(255,Selected,0))
  else (cc_t(I) mod 4 =:= 1 ->
    brush(rgb(0,Selected,255))
  else
     fail
  )), 
  X is floor((cc_x(I)-G_PosX)*G_Zoom),
  Y is floor((cc_y(I)-G_PosY)*G_Zoom),
  Size is floor(10*G_Zoom),
  (cc_t(I) < 100 ->
    rect(X-Size,Y-Size,X+Size,Y+Size)
  else 
    ellipse(X-Size//2,Y-Size//2,X+Size//2,Y+Size//2)
  ).

draw_shell(I,Clean):-
  (Clean =:= 1 ->
    brush(system_color(window))
  else 
    brush(rgb(0,0,0))
  ), 
  X is floor((tar_x(I)-G_PosX)*G_Zoom),
  Y is floor((tar_y(I)-G_PosY)*G_Zoom),
  Size is floor(5*G_Zoom),
  ellipse(X-Size//2,Y-Size//2,X+Size//2,Y+Size//2).


clean_object(I):-
  brush(system_color(window)),
  X is floor((cc_x(I)-G_PosX)*G_Zoom),
  Y is floor((cc_y(I)-G_PosY)*G_Zoom),
  Size is floor(10*G_Zoom),
  (cc_t(I) < 100 ->
    rect(X-Size,Y-Size,X+Size,Y+Size)
  else 
    ellipse(X-Size//2,Y-Size//2,X+Size//2,Y+Size//2)
   ).

draw_list([H|T],Pos):- 
  (cc_t(H) mod 4 =:= 0 ->
    color_text(_,rgb(255,0,0))
  else 
    color_text(_,rgb(0,0,255))
  ),
  (cc_t(H) < 100 ->
    Text is "Palace (" + born(H)+ ")"
  else
    Text is "Soldier" + (status(H)=:=4->" (move)" else (status(H)//2=:=1->" (attack)" else ""))
  ),
  (cc_t(H) =:= 2 ->
    draw_list(T,Pos)
  else
    text_out(0,Pos,Text+" "+cc_h(H)+"%"),
    draw_list(T,Pos+20)
  ).

find_object(A,B,P,Dist):-
  for(P,0,G_CC-1),
    cc_t(P) mod 4 =\= 2,
    (cc_t(P) < 100 ->
      abs(cc_x(P)-A) < 10+Dist, abs(cc_y(P)-B) < 10+Dist
    else 
      (cc_x(P)-A)**2+(cc_y(P)-B)**2 < (5+Dist)**2
    ).

find_another_object(A,B,Obj,Dist):-
  find_object(A,B,P,Dist),
  P=\=Obj.

find_in_rect(A,B,C,D,P):-
  for(P,0,G_CC-1),
    cc_t(P) mod 4 =\= 2,
    cc_x(P)>A,cc_x(P)<C,
    cc_y(P)>B,cc_y(P)<D.

find_enemy(P,Where,Enemy):-
  for(Enemy,0,G_CC-1),
    cc_t(P) mod 4 =:= 1-(cc_t(Enemy) mod 4),
     ( Where=near -> 
        (cc_x(P)-cc_x(Enemy))**2 + (cc_y(P)-cc_y(Enemy))**2 < 10000
     ),
     random(100)<50.

find_place(X2, Y2, A, B, P, Dist):-
  ( not(find_another_object(cc_x(P)+A, cc_y(P)+B, P, Dist)) ->
    X2 is cc_x(P)+A,
    Y2 is cc_y(P)+B
  else ( not(find_another_object(cc_x(P)-B , cc_y(P)+A, P, Dist)) ->
    X2 is cc_x(P)-B,
    Y2 is cc_y(P)+A
  else ( not(find_another_object(cc_x(P)+B, cc_y(P)-A, P, Dist)) ->
    X2 is cc_x(P)+B,
    Y2 is cc_y(P)-A
  else
    fail
  ))).

find_new_object(New):-
  (G_CC<G_MaxObj->
    New:=G_CC,
    G_CC:=G_CC+1
  else
    find_dead_obj(New)
  ).

find_dead_obj(P):-
  for(P,0,G_CC-1),
    cc_t(P) mod 4 =:= 2,
    not(in_fire(P)),
    not(selection(|List), element(P, List)).

in_fire(Obj):-
  for(P,0,G_CC-1),
    attack(P) =:= Obj,
    status(P) mod 2 =:= 1.

