makeclass('player')
makeclass('army')

--
-- HORPS game: Walk around the board collecting armies and buffs
-- Captured armies eat each turn
-- Attacking armies select order (random by default)
-- Defending armies are stronger
-- Command: Tame Creature Stack
-- Command: Free Creature Stack
-- 

--
-- Data Structure
--   Army.Count[kind]=number	Where kind is r,p,s
--

function player.interface(actor, place)
    gui.menu_item("cb_north"          ,"Go North")
    gui.menu_item("cb_south"          ,"Go South")
    gui.menu_item("cb_east"           ,"Go East")
    gui.menu_item("cb_west"           ,"Go West")
    gui.menu_item("cb_map"            ,"Show the Map")
    gui.menu_item("cb_conjurerock"    ,"Conjure Rock")
    gui.menu_item("cb_conjurepaper"   ,"Conjure Paper")
    gui.menu_item("cb_conjurescissor" ,"Conjure Scissor")
    gui.menu_item("cb_droprock"       ,"Drop Rock")
    gui.menu_item("cb_droppaper"      ,"Drop Paper")
    gui.menu_item("cb_dropscissor"    ,"Drop Scissor")
    end


function player:cb_conjurerock()    self.Count['r']=self.Count['r']+1 end
function player:cb_conjurepaper()   self.Count['p']=self.Count['p']+1 end
function player:cb_conjurescissor() self.Count['s']=self.Count['s']+1 end

function army.interface(actor,place)
    if place.owner==actor then gui_menu_item("cb_recruit","Recruit");
    else                       gui.menu_item("cb_fight"  ,"Fight");
    end end

-- Rock dulls Scissors            3
-- Rock cripples Lizard           2
-- Paper disproves Spock	  3
-- Paper covers Rock              2
-- Scissors decapitates Lizard    3
-- Scissors cuts Paper            2
-- Lizard eats Paper              3
-- Lizard poisons Spock           2
-- Spock vaporizes Rock           3
-- Spock dismantles Scissors      2
armytypes={'r','p','s'}
armynames={r="Rock Golem",p="Paper Dragon",s="Scissor Beast",l="Fire Lizzard",v="Mr. Spock"}
--advantage={ r={ r=1,   p=1/2, s=3,   l=2,   v=1/3 },
--            p={ r=2,   p=1,   s=1/2, l=1/3, v=3   },
--	      s={ r=1/3, p=2,   s=1,   l=3,   v=1/2 },
--	      l={ r=1/2, p=3,   s=1/3, l=1,   v=2   },
--	      v={ r=3,   p=1/3, s=2,   l=1/2, v=1   } }
advantage={ r={ r=1,   p=1/2, s=2   },
            p={ r=2,   p=1,   s=1/2 },
	    s={ r=1/2, p=2,   s=1   } }

function shuffle(t)
    local s={}
    for i=1, #t do s[i]=t[i] end
    for i=#t,2,-1 do
        local j=math.random(i)
        s[i],s[j]=s[j],s[i]
        end
    return s
    end


--
-- For each creature type, select the optimal target. Select randomly among identical targets.
--
function army.fight0(ak,ac,dk,dc) -- returns number of attacker casualties, defender casualties
  local adv0=advantage[ak][dk]
  local adv1=advantage[dk][ak]
  local rval0=(ac*adv0-dc)/adv0
  if rval0<0 then rval0=0 end
  local rval1=(dc*adv1-rval0)/adv1
  return rval0,rval1
  end

function army.cb_fight(actor,place,dialog)
  for ak,ac in pairs(actor.Count) do -- Should randomize the order
    local enemyk, enemyc
    local acas
    local maxcas=-1
    local Loss0,Loss1
    local whichdk, whichdc
    for dk,dc in pairs(place.Count) do
      local rem0,rem1=army.fight0(ak,ac,dk,dc)
      Loss0=ac-rem0
      Loss1=dc-rem1
      print("When "..ac.." "..ak.." fight "..dc.." "..dk.." they kill "..Loss1.." and suffer "..Loss0)
      if Loss1>maxcas then
        enemyk=dk
	enemyc=dc
        maxcas=Loss1
	acas=Loss1
	end end
    if enemyk then
      print("Army of "..ac.." "..ak.." fights "..enemyc.." "..enemyk..", killing "..Loss1.." and suffering "..Loss0)
      end
    end
  end


function player:cb_droprock(actor) player:droparmy(actor,'r') end
function player:cb_droppaper(actor) player:droparmy(actor,'p') end
function player:cb_dropscissor(actor) player:droparmy(actor,'s') end

function player:droparmy(actor,kind)
  if actor.Count[kind]<=0 then print("No armies of typem "..kind.." available") return end
  local lis=tangible.near(actor,0,true,true)
  if #lis>1 then print("Multiple Tangibles Nearby") return end
  local t=lis[1]
  if t==nil then t=player:buildarmy(actor) end
  t.Count[kind]=t.Count[kind]+1
  pprint(t)
  end
    
function player:newlocation()
  local lis=tangible.near(self,0,false,false)
  print("New Location")
  pprint(lis)
  end

function player:printanimstate()
  local graphic,plane,x,y,z,facing = tangible.animstate(self)
  print("Resulting state: ", graphic, plane, x, y, z, facing)
  end

function player:cb_north()
  tangible.animate(self, {action="walk", dy=1})
  self:newlocation()
  end

function player:cb_south(place, dialog)
  tangible.animate(place, {action="walk", dy=-1})
  self:newlocation()
  end

function player.cb_east(actor, place, dialog)
  tangible.animate(actor, {action="walk", dx=1})
  self:newlocation()
  end

function player:cb_west(actor, place, dialog)
  tangible.animate(actor, {action="walk", dx=-1})
  self:newlocation()
  end


function player:buildarmy(actor)
  local _,pl,ax,ay=tangible.animstate(actor)
  t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='army'}
  local nt=tangible.build(t)
  nt.Count={}
  setmetatable(nt.Count,{__index=function(t,k) return 0 end,__newindex=function(t,k,v) if v~=nil and v~=0 then rawset(t,k,v) end end})
  return nt
  end


function player.cb_emit_army(actor,place,dialog)
  local _,pl,ax,ay=tangible.animstate(actor)
  t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='army'}
  pprint("building ",t)
  local nt=tangible.build(t)
  setmetatable(nt,{r=0,p=0,s=0})
  nt.kind='r'
  nt.count=1
  end

function player.cb_emit_buff(actor,place,dialog)
  local _,pl,ax,ay=tangible.animstate(actor)
  t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='buff'}
  pprint("building ",t)
  tangible.build(t)
  end

makeclass('army')
makeclass('buff')

function seq(a,b,c) return a<=b and b<=c or false end
function num2(a) if a<=9 then return " "..a else return a end end

function player.cb_map(actor,place,dialog)
  local rad=2
  scratch={}
  local lis=tangible.near(actor,rad,true,false)
  pprint("cb_map ",lis)
  for _,t in pairs(lis) do
    if t~=actor then print(tangible.getclass(t)) end
    local graphic,plane,x,y,z,facing = tangible.animstate(t)
    local c=tangible.getclass(t)
    local dx,dy=tangible.xyz(t)
    local offset=(dy+rad)*(rad*2+1)+dx+rad
    local cl=tangible.getclass(t)
    local kind=t.kind or 'Nil'
    if not scratch[offset] then scratch[offset]={} end
    if not scratch[offset].count then scratch[offset].count={} end
    if not scratch[offset].count[kind] then scratch[offset].count[kind]=0 end
    if t.count then scratch[offset].count[kind]=scratch[offset].count[kind]+t.count end
    end
  for dy=-rad,rad do for line=0,1 do
    local lbuf=""
    for dx=-rad,rad do
      local offset=(dy+rad)*(rad*2+1)+dx+rad
      if     line==0 then lbuf=lbuf.."----"
      elseif line==1 then
        local kc=0
        local tc=0
        local kstr=' '
        if scratch[offset] then for k,c in pairs(scratch[offset].count) do if c>0 then
          kc=kc+1
          tc=tc+c
          if kc>1 then kstr='M' else kstr=k end
          end end end
        lbuf=lbuf.."-"..kstr..(tc>0 and num2(tc) or "  ")
        end end
    lbuf=lbuf.."-"
    print(lbuf)
    end end
  lbuf=""
  for dx=-rad,rad do lbuf=lbuf.."----" end
  lbuf=lbuf.."-"
  print(lbuf)
  print("In Player "..tangible.id(actor)..":")
  for k,v in pairs(actor.Count) do print("  "..k.." "..v) end
  local lis=tangible.near(actor,0,true,true)
  for k,v in pairs(lis) do
    print("In Army "..tangible.id(v)..":")
    for k2,v2 in pairs(v.Count) do print("  "..k2.." "..v2) end
    end
  end

    

