Code / Questions re: code for Testing Grounds Portal


If you are interested in helping with tiles, vaults, patches or documentation, this is the place for that.

7hm

Snake Sneak

Posts: 109

Joined: Wednesday, 2nd February 2011, 03:20

Post Monday, 21st March 2011, 21:08

Code / Questions re: code for Testing Grounds Portal

Code / coding questions / vaults related to Testing Grounds Portal concept.

As galehar said, it would be better to keep code and implementation in this subforum.

The original idea with some additional thoughts through the thread is here.

The code (as of this particular moment in time, I'll post updated stuff every so often) is here:

  Code:
###################################################################
#
# Testing Ground Portal
# Created by 7hm.

# Flavour: God challenge arena.  Defeat successive waves of monsters
# to collect your reward.
#
#
# PLAN:
#
# Create monsters waves
# Create better randomized monster lists
# Create a random weapon generator
# Create a better random name generator
# Create "boss wave" monster wave
# Randomize # of monster waves
# Randomize # of monsters within waves
# Create more entry portals
# Create more destination portals
# Remove the v_mon_die_call hack if possible
# Create more flavour
# Create crowd noises effect - with possible god intervention effects?
# Remove v_guard as the defining variable and replace it with v_god
# Add error checking where applicable
# Create a template for addition of new vaults (both entry and destination)
# Create a loot table (with adjustment for # of waves, difficulty of waves
# .. god selection and depth found)
# Reward player with loot when all the monsters are defeated
# Add special casing (that works, unlike what I have now) for abyss
# .. and other special effects
# Create a random "status" effect generator so that monsters may be spawned
# .. berserking or otherwise, depending on what the situation allows
# Add tiles for the portal entry (both inactive - but not used, which
# .. will use "stone_arch" as now - and active)
#

{{
-------------------------------------------------------------------
-- Variables
-- : set up the global variables that are used by the various functions
-------------------------------------------------------------------
-- current wave #
v_wave_num = 0
-- number of monsters in the current wave
v_wave_qty = 0
-- how many times callback.dest_mons_die has been called (for that hack I
-- note in the function itself
v_mon_die_call = 0
-- the guardian type (which is used to select monster lists)
-- REMOVE THIS AND REPLACE IT WITH v_god
v_guard = nil
-- the destination - will pick easy / hard maps etc
v_dest = nil
-- the god choice - random with some weighting situationally
v_god = nil

-------------------------------------------------------------------
-- Random Generator Functions
-- : functions that are called to return randomized information
-------------------------------------------------------------------

-- random name generator (I know this could be done better, but
-- for now its fine) it returns a random name (25 possible combos)
function rdm_guard_name()
  local a , b
  -- first part of the name
  if crawl.one_chance_in(5) then
    a = "Joh"
  elseif crawl.one_chance_in(4) then
    a = "Din"
  elseif crawl.one_chance_in(3) then
    a = "Lai"
  elseif crawl.one_chance_in(2) then
    a = "Arn"
  else
    a = "Toms"
  end
  -- second part of the name
  if crawl.one_chance_in(5) then
    b = "tu"
  elseif crawl.one_chance_in(4) then
    b = "parn"
  elseif crawl.one_chance_in(3) then
    b = "ghie"
  elseif crawl.one_chance_in(2) then
    b = "yul"
  else
    b = "rins"
  end
  -- return the name
  return a .. b
end

-- EVENTUALLY REMOVE THIS, REPLACE WITH THE GOD AS THE RANDOM SELECTOR
-- This picks the guardian monster --- at the moment it's just random, but
-- it should be based on depth etc on top of randomization
function rdm_guardian()
  -- v_guard is used to create both the monster_list
  -- and tie the destination portal into the guardian monster for flavour
  if crawl.one_chance_in(2) then
     v_guard = "goblin"
     return "goblin name:" .. rdm_guard_name() .." name_replace"
  else
     v_guard = "kobold"
     return "kobold name:" .. rdm_guard_name() .." name_replace"
  end
end

-- This function returns a random god selection based on various possible
-- reasons to call the function. 
-- (At the moment the reasons are: "all" - if you just want to randomly pick
-- a god, "arena" to choose the Training Grounds patron and "cheer" to be
-- used in crowd functionality when that gets implemented.)
function rdm_god(purpose)
  local all_rand = crawl.random_range(1,18)
  local all_gods = {
    [1] = "Zin", [2] = "The Shining One", [3] = "Kikubaaqudgha",
    [4] = "Yredelemnul", [5] = "Xom", [6] = "Vehumet",
    [7] = "Okawaru", [8] = "Makhleb", [9] = "Sif Muna",
    [10] = "Trog", [11] = "Nemelex Xobeh", [12] = "Elyvilon",
    [13] = "Lugonu", [14] = "Beogh", [15] = "Fedhas",
    [16] = "Cheibriados", [17] = "Ashenzari", [18] = "Jiyva"
  }
  local arena_rand = crawl.random_range(1,6)
  local arena_gods = {
    [1] = all_gods[7], [2] = all_gods[10], [3] = all_gods[4],
    [4] = all_gods[16], [5] = all_gods[8], [6] = all_gods[2],
  }
  local cheer_rand = crawl.random_range(1,10)
  local cheer_gods = {
    [1] = all_gods[2], [2] = all_gods[3], [3] = all_gods[4],
    [4] = all_gods[5], [5] = all_gods[6], [6] = all_gods[7],
    [7] = all_gods[8], [8] = all_gods[10], [9] = all_gods[11],
    [10] = all_gods[16]
  }
 
  if purpose == "all" then
    return all_gods[all_rand]
  elseif purpose == "arena" then
    v_god = arena_gods[arena_rand]
    return v_god
  elseif purpose == "cheer" then
    return cheer_gods[cheer_rand]
  else
    error("No purpose arg given to rdm_god(purpose); Trog is default")
    return all_gods[10]
  end
end


-- NOT ACTUALLY DOING ANYTHING RIGHT NOW
-- This picks the destination portal, based on the guardian from
-- create_guardian(); at the moment it just returns the default arena
function rdm_destination(a)
  return "arena_portal_default"
end

-- This function uses v_guard to create a monster list
-- that is tied into the guardian monster (for flavour).  It returns
-- a single monster selection.
function rdm_monster_list(a)
  if a == "goblin" then
     return "goblin / orc / hobgoblin"
  elseif a == "kobold" then
     return "kobold / big kobold"
  else
     error("failure to create monster list")
     return "rat"
  end
end

-------------------------------------------------------------------
-- Portal Entry Functions
-- : used for entry vaults
-------------------------------------------------------------------

-- This sets up the portal entry that will be triggered when the guardian
-- is killed
function setup_arena_portal(e)
 local god = rdm_god("arena")
 local guardian = rdm_guardian()
 local destination = rdm_destination(v_guard) 
  local desc_long = "empty for now, please fill me"
  e.lua_marker('O',
     one_way_stair {
        desc = "pulsating archway",
   desc_long = desc_long,
        entity = 'arena entry',
        dst = destination,
        floor = "stone_arch" })
  e.kfeat("O = stone_arch")
  e.kmons("M = " .. guardian)
 crawl.mpr("Your god is: " .. god .. ".")
end



-- This function is called when the guardian is killed; it pushes flavour text and
-- calls create_arena.portal.
function callback.entry_mons_die(monster, killer_type, killer_index, silent, wizard)
    if killer_type == "reset" then
      crawl.mpr("You feel as though a passage to another world has been closed.")
    else
      crawl.mpr("As you kill the guardian, you sense a passage opening to another world.")
      create_arena_portal(_G)
    end
end

-- This function is called by callback.entry_mons_die, and turns the empty
-- arch into an actual portal.
function create_arena_portal(e)
  local c = dgn.find_marker_positions_by_prop("portal", 1) [1]
  local des_name = "arena_portal"
  local to_feat = "enter_portal_vault"
  dgn.terrain_changed(c.x , c.y , to_feat ,  false, false, false)
  crawl.mpr("vguard is: " .. v_guard .. ".")
end

-------------------------------------------------------------------
-- Portal Destination Functions
-- : used for destination vaults
-------------------------------------------------------------------

-- This function sets up the destination portal tiles, features, etc.
-- You should use symbols from here when designing destination portal vaults.
-- If there is an intersting addition, you can also add to this function
-- so that future vaults may use it.
function arena_setup(e)
  e.kfeat("< = exit_portal_vault")
end

-- THIS NEEDS WORK
-- This function is called when you enter through the portal into the destination vault.
-- It asks you whether or not you want to participate in the arena.  If you do, it sets up
-- the arena.  If you pick no, you end up with an empty vault.
function callback.arena_choice()
  if v_god == nil then
     v_god = rdm_god("arena")
  end
  crawl.god_speaks(v_god,"Welcome, mortal, to the Arena." .. v_god )
  crawl.god_speaks(v_god,"You have here a choice.  You may choose to battle many foes " ..
         "and reap the rewards that are due to a champion.")
  crawl.god_speaks(v_god,"Or you may choose to flee, as a coward, and forfeit a champion's rewards.")
  if crawl.yesno("Will you accept this challenge and fight for your life in THE ARENA?") == true then
     callback.place_monsters()
  end
end

-- THIS NEEDS WORK: It should be passable multiple times rather than a single time.
-- This function actually creates the monsters based on a list of positions (from
-- rdm_monster_list
function callback.place_monsters()
  v_wave_qty = 0
  for slave in iter.slave_iterator("monster", 1) do
     local monster = rdm_monster_list(v_guard)
     dgn.create_monster(slave.x, slave.y, monster)
     v_wave_qty = v_wave_qty + 1
  end
  -- this is just for error checking, should be removed for actual use
  crawl.mpr("The number of monsters is: " .. v_wave_qty)
end

-- This function is called when the monsters die.  It needs some work and I need
-- to ensure that the hack I used isn't achievable in some other way.
function callback.dest_mons_die(monster, killer_type, killer_index, silent, wizard)
  v_mon_die_call = v_mon_die_call + 1
  if killer_type == "reset" then
    crawl.mpr("something wrong")
  else
   -- So... this is a hack because I can't figure out how to get the function to
   -- only call once when a monster is killed, but still call for every monster.
   -- Basically this divides the number of times the function is called into the number
   -- of monsters and actually completes when all the monsters are killed.  It first
   -- error checks to ensure theres no division by zero (though that shouldn't be
   -- possible).
--   crawl.mpr(killer_type)
   if v_wave_qty == 0 then
      error("division by zero in callback.main_mons_die")
      return
   end
   local fake_num = v_mon_die_call / v_wave_qty
   if fake_num == v_wave_qty then
     crawl.mpr("You have killed all " .. v_wave_qty .. " monsters.")
   else
     return
   end
  end
end

-- This function is called by destination vaults and signals that the portal
-- has been completed.
function arena_milestone(e)
  crawl.mark_milestone("br.enter", "entered an Arena.", true)
end
}}

#####################
# Entry portals
#####################
# Bare-bones portal vault entry

NAME:     stuff_that
TAGS:     allow_dup no_monster_gen
ORIENT:  float
PLACE:    D:1
KFEAT:    O = stone_arch
: setup_arena_portal(_G)
{{
lua_marker('O', props_marker { portal = 1 })
}}
MARKER: M = lua: MonPropsMarker:new {monster_dies_lua_key = callback.entry_mons_die}
MAP
.....
.....
..O..
..M..
.....
ENDMAP

####################
# Destination portals
####################

NAME:     arena_generic_default
TAGS:     arena_portal_default allow_dup
ORIENT:  encompass
: arena_setup(_G)
{{
  local choice_prompt = TriggerableFunction:new{func="callback.arena_choice"}
  kill_mons = TriggerableFunction:new{func="callback.dest_mons_die" ,
                repeated = true }
  choice_prompt:add_triggerer(DgnTriggerer:new {type="entered_level"})
  kill_mons:add_triggerer(DgnTriggerer:new {type="monster_dies" , target = "any"})
  lua_marker("<", choice_prompt)
  lua_marker("g", props_marker{ monster = 1 })
  lua_marker("g", kill_mons)
}}
SUBST:    g : .
NSUBST:    e = 2:< / *:.
epilogue{{
arena_milestone(_G)
}}
MAP
ccccccccccccccccccccccccccccccccccccccccccc
c...............................g.........c
c...e......g..........................e...c
c.........................................c
c....................g........cc..........c
c.......g......................cc..g......c
c....cc.........................cc........c
c..cccccc.......g..........g.....cc.......c
c....cc...........................ccc.....c
c..................................ccc....c
c..............cccccc.....................c
c...........g..cccccc.......g.............c
c...g..........cccccc............c........c
c..............cccccc...........cc........c
c..............cccccc...........cc...g....c
c.....cc........................cc........c
c....cccc.......................c.........c
c....cccc.......................c.........c
c.....cc.........<..A...........c.........c
c..............................cc.........c
c...........c...........c......cc.........c
c...........c..........ccc.....cc.........c
c.......ccccccccc.....ccccc....c..........c
c...........c..........ccc................c
c...g...ccccccccc.......c...........g.....c
c...........c.............................c
c...........c.............................c
c.........................................c
c.........g...............g.......g.......c
c.....ccc....ccc..........................c
c......ccc..ccc...........................c
c........cccc..........................g..c
c.........cc..g.......ccccccc.............c
c...................ccccccc.......g.......c
c.....g...........ccc.....................c
c...............cccccccc...g..............c
c.................ccc.....................c
c...................ccccccc...............c
c...e.......g.........ccccccc.........e...c
c.........................................c
ccccccccccccccccccccccccccccccccccccccccccc
ENDMAP


Right now I have a couple issues:

a) the monster_die trigger calls itself for every instance of the monster that exists. So if there are 5 monsters to kill, every time you kill one of them it will trigger the function 5 times (for each monster). My workaround is to create a variable that consists of the number of times the function is called / the number of monsters that exist in the wave (which we know from earlier in the setup). We can then check that against the total number of monsters to determine whether or not they've all been killed, and ignore all the extraneous calls. We're still calling the function far more times than we should though. I assume I'm setting this up wrong in some way, any suggestions?

b) how do we count an abyssing as a kill? I don't want you to be able to abyss the guardian and get the portal, but I do want you to be able to abyss the individual monsters from within the portal itself and still get the loot. Right now the exact opposite is happening, and I'm not really sure why. (I just copied the code without really understanding it, from the slime branch and jiyva stuff).

7hm

Snake Sneak

Posts: 109

Joined: Wednesday, 2nd February 2011, 03:20

Post Tuesday, 22nd March 2011, 18:43

Re: Code / Questions re: code for Testing Grounds Portal

e: I posted new code but I realized something and am going back to make a major revision.

7hm

Snake Sneak

Posts: 109

Joined: Wednesday, 2nd February 2011, 03:20

Post Tuesday, 22nd March 2011, 19:48

Re: Code / Questions re: code for Testing Grounds Portal

  Code:
###################################################################
#
# Testing Ground Portal
# Created by 7hm.

# Flavour: God challenge arena.  Defeat successive waves of monsters
# to collect your reward.
#
#
# PLAN:
#
# Create monsters waves
# Create better randomized monster lists
# Create "boss wave" monster wave
# Randomize # of monster waves
# Randomize # of monsters within waves
# Create more entry portals
# Create more destination portals
# Remove the v_mon_die_call hack if possible
# Create more flavour
# Create crowd noises effect - with possible god intervention effects?
# Add error checking where applicable
# Create a template for addition of new vaults (both entry and destination)
# Create a loot table (with adjustment for # of waves, difficulty of waves
# .. god selection and depth found)
# Reward player with loot when all the monsters are defeated
# Add special casing (that works, unlike what I have now) for abyss
# .. and other special effects
# Create a random "status" effect generator so that monsters may be spawned
# .. berserking or otherwise, depending on what the situation allows
# Add tiles for the portal entry (both inactive - but not used, which
# .. will use "stone_arch" as now - and active)
#
#
# COMPLETED:
#
# rdn_guard_name
# random name generator - pulls from two tables, so high degree of
# .. randomization / perhaps add a possible third small table (c)
#
#

{{
-------------------------------------------------------------------
-- General Variables
-- : set up the global variables that are used by the various functions
-------------------------------------------------------------------
-- current wave #
v_wave_num = 0
-- number of monsters in the current wave (it ends up being randomized)
v_wave_qty = 0
-- how many times callback.dest_mons_die has been called (for that hack I
-- note in the function itself
v_mon_die_call = 0
-- the total number of monsters that have been set up in the map file
v_total_wave_qty = 0
-- the destination - will pick easy / hard maps etc
v_dest = nil
-- the god choice - random with some weighting situationally
v_god = nil

-------------------------------------------------------------------
-- Monster Lists
-- : set up the monster lists for each arena god
-- [1] refers to the basic mobs that will be most common
-- [2] refers to the basic mobs that are a bit rarer - also used as
-- .. guardians
-- [3] refers to bosses / uniques
-------------------------------------------------------------------

v_god_mob = {
------------ OKAWARU -------------
  ["Okawaru"] = {
    [1] = {"rat","human","goblin"},
    [2] = {"orc warrior","orc knight", "dragon"},
    [3] = {"ogre","orc warlord"}
  },
------------ TROG -------------
  ["Trog"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ YRED -------------
  ["Yredelemnul"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ TSO -------------
  ["The Shining One"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ Beogh -------------
  ["Beogh"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ Xom -------------
  ["Xom"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ Makhleb -------------
  ["Makhleb"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  },
------------ Cheibriado -------------
  ["Cheibriado"] = {
    [1] = {"kobold","elf","orc"},
    [2] = {"yaktaur","big kobold", "storm dragon"},
    [3] = {"orc priest","ogre mage"}
  }
}

-------------------------------------------------------------------
-- Random Generator Functions
-- : functions that are called to return randomized information
-------------------------------------------------------------------

-- random name generator
function rdm_guard_name()
  local a , b , c
  local rand_a_range = crawl.random_range(1,84)
  local rand_name_a = {
    "Ab","Ar","At","Ba","Bo","Bry",
    "Bih","Cie","Cha","Cho","Dea",
    "Dri","De","Dlo","Eze","E","A",
    "Fle","Fa","Fy","Ga","Goe","Giy",
    "Hu","Hou","Hae","I","Ie","Ira",
    "Je","Ju","Ko","Ka","Lo","La",
    "Lae","Mo","Muo","Miu","Na","Ne",
    "O","Oro","Oh","Pi","Pia","Pyo",
    "Pha","Phe","Qe","Qui","Qua","Quo",
    "Qio","Qa","Ra","Reh","Rya","Rio",
    "Sae","Si","So","Shi","Shy","Sra",
    "Tu","Thi","Thu","Ty","Tae","U",
    "Uva","Urh","Uy","Wa","Wri","Wo",
    "Xi","Xo,","Yu","Yi","Za","Zre"
    }
  a = rand_name_a[rand_a_range]

  local rand_b_range = crawl.random_range(1,60)
  local rand_name_b = {
    "bir","bet","bug","chor","cim",
    "dan","dyw","dom","ful","fyur",
    "fop","gag","grin","gop","hix",
    "hyt","hrom","jik","jew","kil",
    "kes","kav","luv","lyb","lear",
    "min","man","mud","mok","nil",
    "nuw","nor","nat","pran","poz",
    "pel","par","quar","quin","qat",
    "ris","ros","ruj","ran","rop",
    "sab","son","sun","syet","tri",
    "tha","the","trit","van","vaw",
    "vim","vrae","zed","zot","zah"
    }
  b = rand_name_b[rand_b_range]
  -- return the name
  return a .. b
end

-- EVENTUALLY REMOVE THIS, REPLACE WITH THE GOD AS THE RANDOM SELECTOR
-- This picks the guardian monster --- at the moment it's just random, but
-- it should be based on depth etc on top of randomization
function rdm_guardian()
  guardian = callback.monster_list(2)
  return guardian .. " name:" .. rdm_guard_name()
end

-- This function returns a random god selection based on various possible
-- reasons to call the function. 
-- (At the moment the reasons are: "all" - if you just want to randomly pick
-- a god, "arena" to choose the Training Grounds patron and "cheer" to be
-- used in crowd functionality when that gets implemented.)
function rdm_god(purpose)
  local all_rand = crawl.random_range(1,18)
  local all_gods = {
    [1] = "Zin", [2] = "The Shining One", [3] = "Kikubaaqudgha",
    [4] = "Yredelemnul", [5] = "Xom", [6] = "Vehumet",
    [7] = "Okawaru", [8] = "Makhleb", [9] = "Sif Muna",
    [10] = "Trog", [11] = "Nemelex Xobeh", [12] = "Elyvilon",
    [13] = "Lugonu", [14] = "Beogh", [15] = "Fedhas",
    [16] = "Cheibriados", [17] = "Ashenzari", [18] = "Jiyva"
  }
  local arena_rand = crawl.random_range(1,7)
  local arena_gods = {
    [1] = all_gods[7], [2] = all_gods[10], [3] = all_gods[4],
    [4] = all_gods[16], [5] = all_gods[8], [6] = all_gods[2],
    [7] = all_gods[5]
  }
  local cheer_rand = crawl.random_range(1,10)
  local cheer_gods = {
    [1] = all_gods[2], [2] = all_gods[3], [3] = all_gods[4],
    [4] = all_gods[5], [5] = all_gods[6], [6] = all_gods[7],
    [7] = all_gods[8], [8] = all_gods[10], [9] = all_gods[11],
    [10] = all_gods[16]
  }
  if purpose == "all" then
    return all_gods[all_rand]
  elseif purpose == "arena" then
    v_god = arena_gods[arena_rand]
    return v_god
  elseif purpose == "cheer" then
    return cheer_gods[cheer_rand]
  else
    error("No purpose arg given to rdm_god(purpose); Trog is default")
    return all_gods[10]
  end
end


-- NOT ACTUALLY DOING ANYTHING RIGHT NOW
-- This picks the destination portal, based on the guardian from
-- create_guardian(); at the moment it just returns the default arena
function rdm_destination()
  return "arena_portal_default"
end

-- This function calls on the lists (by god) and returns a random monster
-- based on the god and the mob type chosen (easy mob, hard mob, boss mob)
function callback.monster_list(a)
  local mob = v_god_mob
  local range = crawl.random_range(1,3)
  local monster = mob[v_god][a][range]
  if monster == nil then
    monster = mob[v_god][a][1]
    crawl.mpr("monster's coming nil yeah")
      if monster == nil then
        crawl.mpr("monster coming up super nil")
        return "rat"
      end
  end
  return monster 
end


-------------------------------------------------------------------
-- Portal Entry Functions
-- : used for entry vaults
-------------------------------------------------------------------

-- This sets up the portal entry
function setup_arena_portal(e)
  local god = rdm_god("arena")
  local guardian = rdm_guardian()
  local destination = rdm_destination() 
  local desc_long = "empty for now, please fill me"
  e.lua_marker('O',
     one_way_stair {
        desc = "pulsating archway",
   desc_long = desc_long,
        entity = 'arena entry',
        dst = destination,
        floor = "stone_arch" })
  e.kfeat("O = stone_arch")
  e.kmons("M = " .. guardian)
  crawl.mpr("Your god is: " .. god .. ".")
end



-- This function is called when the guardian is killed; it pushes flavour text and
-- calls create_arena.portal.
function callback.entry_mons_die(monster, killer_type, killer_index, silent, wizard)
    if killer_type == "reset" then
      crawl.mpr("You feel as though a passage to another world has been closed.")
    else
      crawl.mpr("As you kill the guardian, you sense a passage opening to another world.")
      create_arena_portal(_G)
    end
end

-- This function is called by callback.entry_mons_die, and turns the empty
-- arch into an actual portal.
function create_arena_portal(e)
  local c = dgn.find_marker_positions_by_prop("portal", 1) [1]
  local des_name = "arena_portal"
  local to_feat = "enter_portal_vault"
  dgn.terrain_changed(c.x , c.y , to_feat ,  false, false, false)
  crawl.mpr("vguard is: " .. v_guard .. ".")
end

-------------------------------------------------------------------
-- Portal Destination Functions
-- : used for destination vaults
-------------------------------------------------------------------

-- This function sets up the destination portal tiles, features, etc.
-- You should use symbols from here when designing destination portal vaults.
-- If there is an intersting addition, you can also add to this function
-- so that future vaults may use it.
function arena_setup(e)
  e.kfeat("< = exit_portal_vault")
end

-- THIS NEEDS WORK
-- This function is called when you enter through the portal into the destination vault.
-- It asks you whether or not you want to participate in the arena.  If you do, it sets up
-- the arena.  If you pick no, you end up with an empty vault.
function callback.arena_choice()
  if v_god == nil then
     v_god = rdm_god("arena")
  end
  crawl.god_speaks(v_god,"Welcome, mortal, to the Arena." .. v_god )
  crawl.god_speaks(v_god,"You have here a choice.  You may choose to battle many foes " ..
         "and reap the rewards that are due to a champion.")
  crawl.god_speaks(v_god,"Or you may choose to flee, as a coward, and forfeit a champion's rewards.")
  if crawl.yesno("Will you accept this challenge and fight for your life in THE ARENA?") == true then
     callback.place_monsters()
  end
end

-- THIS NEEDS WORK: It should be passable multiple times rather than a single time.
-- This function actually creates the monsters based on a list of positions (from
-- rdm_monster_list
function callback.place_monsters()
  v_wave_qty = 0
  local mons_type = 1
  local max_wave_size = crawl.random_range(6,10)
  for slave in iter.slave_iterator("monster", 1) do
     if v_wave_qty <= max_wave_size then
       if crawl.one_chance_in(4) then
         mons_type = 2
       end
       local monster = callback.monster_list(mons_type)
       dgn.create_monster(slave.x, slave.y, monster)
       v_wave_qty = v_wave_qty + 1
       difficulty = 1
     end
     v_total_wave_qty = v_total_wave_qty + 1
  end
  -- this is just for error checking, should be removed for actual use
  crawl.mpr("The number of monsters is: " .. v_wave_qty)
end

-- This function is called when the monsters die.  It needs some work and I need
-- to ensure that the hack I used isn't achievable in some other way.
function callback.dest_mons_die(monster, killer_type, killer_index, silent, wizard)
  v_mon_die_call = v_mon_die_call + 1
  if killer_type == "reset" then
    crawl.mpr("something wrong")
  else
   -- So... this is a hack because I can't figure out how to get the function to
   -- only call once when a monster is killed, but still call for every monster.
   -- Basically this divides the number of times the function is called into the number
   -- of monsters and actually completes when all the monsters are killed.  It first
   -- error checks to ensure theres no division by zero (though that shouldn't be
   -- possible).
--   crawl.mpr(killer_type)
   if v_wave_qty == 0 then
      error("division by zero in callback.main_mons_die")
      return
   end
   local fake_num = v_mon_die_call / v_total_wave_qty
   if fake_num == v_wave_qty then
     crawl.mpr("You have killed all " .. v_wave_qty .. " monsters.")
   else
     return
   end
  end
end

-- This function is called by destination vaults and signals that the portal
-- has been completed.
function arena_milestone(e)
  crawl.mark_milestone("br.enter", "entered an Arena.", true)
end
}}

#####################
# Entry portals
#####################
# Bare-bones portal vault entry

NAME:     stuff_that
TAGS:     allow_dup no_monster_gen
ORIENT:  float
PLACE:    D:1
KFEAT:    O = stone_arch
: setup_arena_portal(_G)
{{
lua_marker('O', props_marker { portal = 1 })
}}
MARKER: M = lua: MonPropsMarker:new {monster_dies_lua_key = callback.entry_mons_die}
MAP
.....
.....
..O..
..M..
.....
ENDMAP

####################
# Destination portals
####################

NAME:     arena_generic_default
TAGS:     arena_portal_default allow_dup
ORIENT:  encompass
: arena_setup(_G)
{{
  local choice_prompt = TriggerableFunction:new{func="callback.arena_choice"}
  kill_mons = TriggerableFunction:new{func="callback.dest_mons_die" ,
                repeated = true }
  choice_prompt:add_triggerer(DgnTriggerer:new {type="entered_level"})
  kill_mons:add_triggerer(DgnTriggerer:new {type="monster_dies" , target = "any"})
  lua_marker("<", choice_prompt)
  lua_marker("g", props_marker{ monster = 1 })
  lua_marker("g", kill_mons)
}}
SUBST:    g : .
NSUBST:    e = 2:< / *:.
epilogue{{
arena_milestone(_G)
}}
MAP
ccccccccccccccccccccccccccccccccccccccccccc
c...............................g.........c
c...e......g..........................e...c
c.........................................c
c....................g........cc..........c
c.......g......................cc..g......c
c....cc.........................cc........c
c..cccccc.......g..........g.....cc.......c
c....cc...........................ccc.....c
c..................................ccc....c
c..............cccccc.....................c
c...........g..cccccc.......g.............c
c...g..........cccccc............c........c
c..............cccccc...........cc........c
c..............cccccc...........cc...g....c
c.....cc........................cc........c
c....cccc.......................c.........c
c....cccc.......................c.........c
c.....cc.........<..A...........c.........c
c..............................cc.........c
c...........c...........c......cc.........c
c...........c..........ccc.....cc.........c
c.......ccccccccc.....ccccc....c..........c
c...........c..........ccc................c
c...g...ccccccccc.......c...........g.....c
c...........c.............................c
c...........c.............................c
c.........................................c
c.........g...............g.......g.......c
c.....ccc....ccc..........................c
c......ccc..ccc...........................c
c........cccc..........................g..c
c.........cc..g.......ccccccc.............c
c...................ccccccc.......g.......c
c.....g...........ccc.....................c
c...............cccccccc...g..............c
c.................ccc.....................c
c...................ccccccc...............c
c...e.......g.........ccccccc.........e...c
c.........................................c
ccccccccccccccccccccccccccccccccccccccccccc
ENDMAP


Monster lists are implemented and called based on god choice. There are three monster types - easy mobs, hard mobs, and boss mobs. Guardians are hard mobs. Not using god appropriate monsters atm.

The number of monsters in each wave is now randomized (from a range). I'm going to have the system track the number of waves / number of monsters / total difficulty of monsters and loot will be based on that.

Still only have a single wave and no loot.

Names for the guardians are significantly more random, using a simple a..b design with about 5k possible names.

This code is functional BUT - it appears to generate a lua script error every once in a while. Nothing breaks, the entire portal still works, but I can't figure it out.

The message is:

Lua error[string "global_prelude"] :191: attempt to index field '?' (a nil value)

7hm

Snake Sneak

Posts: 109

Joined: Wednesday, 2nd February 2011, 03:20

Post Wednesday, 23rd March 2011, 16:21

Re: Code / Questions re: code for Testing Grounds Portal

  Code:
###################################################################
#
# Testing Ground Portal
# Created by 7hm.
#
# Flavour: God challenge arena.  Defeat successive waves of monsters
# to collect your reward.
#
#
# PLAN:
#
# Create monsters waves
# Create better randomized monster lists
# Create "boss wave" monster wave
# Randomize # of monster waves
# Randomize # of monsters within waves
# Create more entry portals
# Create more destination portals
# Remove the v_mon_die_call hack if possible
# Create more flavour
# Create crowd noises effect - with possible god intervention effects?
# Remove v_guard as the defining variable and replace it with v_god
# Add error checking where applicable
# Create a template for addition of new vaults (both entry and destination)
# Create a loot table (with adjustment for # of waves, difficulty of waves
# .. god selection and depth found)
# Reward player with loot when all the monsters are defeated
# Add special casing (that works, unlike what I have now) for abyss
# .. and other special effects
# Create a random "status" effect generator so that monsters may be spawned
# .. berserking or otherwise, depending on what the situation allows
# Add tiles for the portal entry (both inactive - but not used, which
# .. will use "stone_arch" as now - and active)
# Create string create for monster generation to make it easier on the easy and
# .. easier to create monster effects without breaking the entire portal#
# Create noise using dgn.noisy(loudness,x,y) so that all monsters are aware of
# .. player's position when he enters the arena, and make sure that it gets called
# .. every so often (during crowd cheers?)
#
# COMPLETED:
#
# ## random name generator - pulls from two tables, so high degree of
# .. randomization / perhaps add a possible third small table (c)
# ## create monster list - the monster list is a table in the format
# .. [godname] [mob type] = "monster list"; very easy to add new gods
# ..  or new mob types this way
# ## create monster properties structure -  monster properties are pulled
# .. from a property listing using a string generation function and
# .. include instructions to ensure easy editing and readability
# ## random god selection - creates a table with god names and creates
# .. options for gods to be pulled based on a variety of arguments
# .. passed to the function; the function can be used for crowd cheers,
# .. god effects, and arena choice (potentially more as well)
# .. *** ARENA GOD SELECTION SHOULD ADJUSTED BASED ON DUNGEON FACTORS
# ## setup arena portal - this creates the arena and makes sure that the portal
# .. will exist once the guardian dies *** NOT 100% - flavour etc is needed
# ## arena setup -- the arena is created when you enter the level, and before it
# .. even loads you're given the choice to participate or not.  if you don't,
# .. you'll be in an empty arena, otherwise this function actually calls the
# .. initial monster placement etc
# .. *** NEEDS MORE RANDOMIZATION / FLAVOUR or perhaps exporting choice strings
# .. to a new function
# ## monster placement -- the initial monster placement generates the first wave
# .. of monsters, uses random wave size, and sets up the inital wave tracking
# ## monster death calls -- when a monster dies, this tracks # of kills
# .. *** THIS NEEDS TO CREATE REWARDS, CREATE NEW WAVES
#
#

{{
-------------------------------------------------------------------
-- General Variables
-- : set up the global variables that are used by the various functions
-------------------------------------------------------------------
-- current wave #
v_wave_num = 0
-- number of monsters in the current wave (it ends up being randomized)
v_wave_qty = 0
-- total difficulty level : ongoing
v_total_difficulty = 0
-- desired difficulty level : this is set randomly by the vault maker
v_desired_difficulty = 0
v_low_wave_size = 6
v_high_wave_size = 10
-- how many times callback.dest_mons_die has been called (for that hack I
-- note in the function itself
v_mon_die_call = 0
-- the total number of monsters that have been set up in the map file
v_map_spawn_positions = 0
-- the destination - will pick easy / hard maps etc
v_dest = nil
-- the god choice - random with some weighting situationally
v_god = nil

-------------------------------------------------------------------
-- Monster Properties
-- : set up the monster properties and the function that calls these
-- properties that are used by monster lists
-------------------------------------------------------------------
v_mon_props = {
  ["patrol"] = "patrolling",
  ["gen_awake"] = "generate_awake",
  ["spells_no"] = "spells:.;.;.;.;.;.",
  ["spells_haste"] = "spells:.;.;haste;.;.;.",
  ["spells_oka_hard"] = "spells:.;haste;haste;.;.;."
}

-- This generates the monster property string.  If it's a simple string,
-- it just returns the string directly.  You can also add special casing
-- for more complicated strings so that the monster properties don't get
-- ridiculously long strings
function generate_string(a)
  if a == nil then
    -- Doesn't matter what you put here, the program won't open if this
    -- value is nil
    error("generate_string(a) is a nil value")
  end
-- Put all special casing for string properties here
  if a == "guard" then
     string = v_mon_props["gen_awake"] .. " " .. v_mon_props["patrol"]
     return string
  elseif a == "spells_oka_mob" then
     if crawl.one_chance_in(3) then
       string = v_mon_props["spells_haste"]
     else
       string = v_mon_props["spells_no"]
     end
     return string
  end
-- otherwise just return the string
  string = v_mon_props[a]
  return string
end

-------------------------------------------------------------------
-- Monster Lists
-- : set up the monster lists for each arena god
-- [1] refers to the basic mobs that will be most common
-- [2] refers to the basic mobs that are a bit rarer
-- [3] refers to bosses / uniques
-- [4] refers to guardian monsters
-- .. you can add additional lists if you like, and call them from
-- various functions using callback.monster_list(#)
-------------------------------------------------------------------
-- Instructions for adding monsters:
-- a) if using an existing god / mob type, add a line to the appropriate
-- section
-- b) ensure that you add the appropriate " / " if the monster is to
-- be chosen from an existing list
-- c) use generate_string"property" to add all monster properties
-- d) use a space before and after the monster name, because the properties
-- do not have spaces
-- e) please comment (as seen below) so that the monsters are obvious
-- because without it the monsters can kind of run together
-- f) if you need to use multiple properties that are already defined, try
-- to either use an existing special case property, or create a new one
-- and define it in generate_string()
-------------------------------------------------------------------
v_god_mob = {
  ------------ OKAWARU -------------
  ["Okawaru"] = {
    [1] = generate_string("gen_awake") .. " human " ..--HUMAN
          generate_string("spells_oka_mob") .. " / " ..
          generate_string("gen_awake") .. " centaur " ..--CENTAUR
          generate_string("spells_haste") .. " / " ..
          generate_string("gen_awake") .. " spriggan " ..--SPRIGGAN
          generate_string("spells_haste") .. " / " ..
          generate_string("gen_awake") .. " deep elf fighter " ..--DEEP ELF FIGHTER
          generate_string("spells_haste"),
    [2] = generate_string("gen_awake") .. " deep elf knight " ..--DEEP ELF KNIGHT
          generate_string("spells_oka_hard") .. " / " ..
          generate_string("gen_awake") .. " draconian " ..-- DRACONIAN
          generate_string("spells_oka_hard"),
    [3] = "Maud / Jozef / Donald / Wiglaf",
    [4] = generate_string("guard") .. " elf " .. generate_string("spells_haste") .. " / " ..
     generate_string("guard") .. " centaur " .. generate_string("spells_haste")
  },
  ------------ TROG -------------
  ["Trog"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "ettin / stone giant / Snorg",
    [4] = "elf spells:.;.;haste;.;.;."
  },
  ------------ YRED -------------
  ["Yredelemnul"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "phantasmal warrior / rock troll / minotaur",
    [3] = "bone dragon / stone giant",
    [4] = "elf spells:.;.;haste;.;.;."
  },
  ------------ TSO -------------
  ["The Shining One"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "ettin / stone giant",
    [4] = "elf spells:.;.;haste;.;.;."
  },
  ------------ Beogh -------------
  ["Beogh"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "ettin / stone giant",
    [4] = "elf spells:.;.;haste;.;.;."
  },
------------ Xom -------------
  ["Xom"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "ettin / stone giant",
    [4] = "elf spells:.;.;haste;.;.;."
  },
------------ Makhleb -------------
  ["Makhleb"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "Frances / Rupert",
    [4] = "elf spells:.;.;haste;.;.;."
  },
------------ Cheibriado -------------
  ["Cheibriado"] = {
    [1] = "ogre / troll / hill giant / two-headed ogre \
           / grizzly bear",
    [2] = "iron troll / rock troll / minotaur",
    [3] = "ettin / stone giant",
    [4] = "elf spells:.;.;haste;.;.;."
  }
}

-------------------------------------------------------------------
-- Random Generator Functions
-- : functions that are called to return randomized information
-------------------------------------------------------------------

-- random name generator
function rdm_guard_name()
  local a , b , c
  local rand_a_range = crawl.random_range(1,84)
  local rand_name_a = {
    "Ab","Ar","At","Ba","Bo","Bry",
    "Bih","Cie","Cha","Cho","Dea",
    "Dri","De","Dlo","Eze","E","A",
    "Fle","Fa","Fy","Ga","Goe","Giy",
    "Hu","Hou","Hae","I","Ie","Ira",
    "Je","Ju","Ko","Ka","Lo","La",
    "Lae","Mo","Muo","Miu","Na","Ne",
    "O","Oro","Oh","Pi","Pia","Pyo",
    "Pha","Phe","Qe","Qui","Qua","Quo",
    "Qio","Qa","Ra","Reh","Rya","Rio",
    "Sae","Si","So","Shi","Shy","Sra",
    "Tu","Thi","Thu","Ty","Tae","U",
    "Uva","Urh","Uy","Wa","Wri","Wo",
    "Xi","Xo,","Yu","Yi","Za","Zre"
    }
  a = rand_name_a[rand_a_range]

  local rand_b_range = crawl.random_range(1,60)
  local rand_name_b = {
    "bir","bet","bug","chor","cim",
    "dan","dyw","dom","ful","fyur",
    "fop","gag","grin","gop","hix",
    "hyt","hrom","jik","jew","kil",
    "kes","kav","luv","lyb","lear",
    "min","man","mud","mok","nil",
    "nuw","nor","nat","pran","poz",
    "pel","par","quar","quin","qat",
    "ris","ros","ruj","ran","rop",
    "sab","son","sun","syet","tri",
    "tha","the","trit","van","vaw",
    "vim","vrae","zed","zot","zah"
    }
  b = rand_name_b[rand_b_range]
  -- return the name
  return a .. b
end

-- This picks the guardian monster --- at the moment it's just random, but
-- it should be based on depth etc on top of randomization
-- ???? maybe eliminate this and replace it with mons props etc, it's kind of
-- out of place right now
function rdm_guardian()
  guardian = callback.monster_list(4)
  return guardian .. " name:" .. rdm_guard_name()
end

-- This function returns a random god selection based on various possible
-- reasons to call the function. 
-- (At the moment the reasons are: "all" - if you just want to randomly pick
-- a god, "arena" to choose the Training Grounds patron and "cheer" to be
-- used in crowd functionality when that gets implemented.)
function rdm_god(purpose)
  local all_rand = crawl.random_range(1,18)
  local all_gods = {
    [1] = "Zin", [2] = "The Shining One", [3] = "Kikubaaqudgha",
    [4] = "Yredelemnul", [5] = "Xom", [6] = "Vehumet",
    [7] = "Okawaru", [8] = "Makhleb", [9] = "Sif Muna",
    [10] = "Trog", [11] = "Nemelex Xobeh", [12] = "Elyvilon",
    [13] = "Lugonu", [14] = "Beogh", [15] = "Fedhas",
    [16] = "Cheibriados", [17] = "Ashenzari", [18] = "Jiyva"
  }
  local arena_rand = crawl.random_range(1,7)
  local arena_gods = {
    [1] = all_gods[7], [2] = all_gods[10], [3] = all_gods[4],
    [4] = all_gods[16], [5] = all_gods[8], [6] = all_gods[2],
    [7] = all_gods[5]
  }
  local cheer_rand = crawl.random_range(1,10)
  local cheer_gods = {
    [1] = all_gods[2], [2] = all_gods[3], [3] = all_gods[4],
    [4] = all_gods[5], [5] = all_gods[6], [6] = all_gods[7],
    [7] = all_gods[8], [8] = all_gods[10], [9] = all_gods[11],
    [10] = all_gods[16]
  }
  if purpose == "all" then
    return all_gods[all_rand]
  elseif purpose == "arena" then
   -- v_god = arena_gods[arena_rand]
    v_god = arena_gods[1]-- this is just for testing
    return v_god
  elseif purpose == "cheer" then
    return cheer_gods[cheer_rand]
  else
    error("No purpose arg given to rdm_god(purpose); Trog is default")
    return all_gods[10]
  end
end


-- NOT ACTUALLY DOING ANYTHING RIGHT NOW
-- This picks the destination portal, based on the guardian from
-- create_guardian(); at the moment it just returns the default arena
function rdm_destination()
  return "arena_portal_default"
end

-------------------------------------------------------------------
-- Portal Entry Functions
-- : used for entry vaults
-------------------------------------------------------------------

-- This sets up the portal entry
function setup_arena_portal(e)
  local god = rdm_god("arena")
  local guardian = rdm_guardian()
  local destination = rdm_destination() 
  local desc_long = "empty for now, please fill me"
  e.lua_marker('O',
     one_way_stair {
        desc = "pulsating archway",
   desc_long = desc_long,
        entity = 'arena entry',
        dst = destination,
        floor = "stone_arch" })
  e.kfeat("O = stone_arch")
  e.kmons("M = " .. guardian)
  crawl.mpr("Your god is: " .. god .. ".")
end



-- This function is called when the guardian is killed; it pushes flavour text and
-- calls create_arena.portal.
function callback.entry_mons_die(monster, killer_type, killer_index, silent, wizard)
    if killer_type == "reset" then
      crawl.mpr("You feel as though a passage to another world has been closed.")
    else
      crawl.mpr("As you kill the guardian, you sense a passage opening to another world.")
      create_arena_portal(_G)
    end
end

-- This function is called by callback.entry_mons_die, and turns the empty
-- arch into an actual portal.
function create_arena_portal(e)
  local c = dgn.find_marker_positions_by_prop("portal", 1) [1]
  local des_name = "arena_portal"
  local to_feat = "enter_portal_vault"
  dgn.terrain_changed(c.x , c.y , to_feat ,  false, false, false)
  crawl.mpr("vguard is: " .. v_guard .. ".")
end

-------------------------------------------------------------------
-- Portal Destination Functions
-- : used for destination vaults
-------------------------------------------------------------------

-- This function sets up the destination portal tiles, features, etc.
-- You should use symbols from here when designing destination portal vaults.
-- If there is an intersting addition, you can also add to this function
-- so that future vaults may use it.
function arena_setup(e)
  e.kfeat("< = exit_portal_vault")
end

-- THIS NEEDS WORK
-- This function is called when you enter through the portal into the destination vault.
-- It asks you whether or not you want to participate in the arena.  If you do, it sets up
-- the arena.  If you pick no, you end up with an empty vault.
function callback.arena_choice(data, triggerable, triggerer, marker, ev)
  v_desired_difficulty = callback.set_difficulty(data.difficulty)
  if v_god == nil then
     v_god = rdm_god("arena")
  end
  crawl.god_speaks(v_god,"Welcome, mortal, to the Arena." .. v_god )
  crawl.god_speaks(v_god,"You have here a choice.  You may choose to battle many foes " ..
         "and reap the rewards that are due to a champion.")
  crawl.god_speaks(v_god,"Or you may choose to flee, as a coward, and forfeit a champion's rewards.")
  if crawl.yesno("Will you accept this challenge and fight for your life in THE ARENA?") == true then
     crawl.mpr("The desired difficulty level is: " .. v_desired_difficulty)
     callback.place_monsters()
  else
     crawl.god_speaks(v_god,v_god .. " is very dissapointed that you will not take the challenge.")
  end
end

-- This function sets the difficulty level (based on depth, nothing level-scaling)
-- right now it's just a random change to the difficulty
function callback.set_difficulty(a)
  a = a + (crawl.random_range(-15,15))
  return a
end

-- This function actually creates the monsters based on a list of positions (from
-- rdm_monster_list
function callback.place_monsters()
  v_wave_qty = 0
  local mons_type = 1
  local max_wave_size = crawl.random_range(v_low_wave_size,v_high_wave_size)
  for slave in iter.slave_iterator("monster", 1) do
     if v_wave_qty <= max_wave_size then
       if crawl.one_chance_in(4) then
         mons_type = 2
         v_total_difficulty = v_total_difficulty + 2
       end
       local monster = callback.monster_list(mons_type)
       dgn.create_monster(slave.x, slave.y, monster)
       v_wave_qty = v_wave_qty + 1
       v_total_difficulty = v_total_difficulty + 1
     mons_type = 1
     end
     v_map_spawn_positions = v_map_spawn_positions + 1
  end
  -- this is just for error checking, should be removed for actual use
  crawl.mpr("The number of monsters is: " .. v_wave_qty ..
            ". The total wave difficulty is: " .. v_total_difficulty)
end

-- This function calls on the monster table and returns a random monster
-- based on the god and the mob type chosen
function callback.monster_list(a)
  local mob = v_god_mob
  local monster = mob[v_god][a]
  if monster == nil then
    monster = mob[v_god][a]
    crawl.mpr("monster's coming nil yeah")
      if monster == nil then
        crawl.mpr("monster coming up super nil")
        return "rat"
      end
  end
  return monster 
end

-- This function is called when the monsters die.  It needs some work and I need
-- to ensure that the hack I used isn't achievable in some other way.
function callback.dest_mons_die(monster, killer_type, killer_index, silent, wizard)
  v_mon_die_call = v_mon_die_call + 1
  if killer_type == "reset" then
    crawl.mpr("something wrong")
  else
   -- So... this is a hack because I can't figure out how to get the function to
   -- only call once when a monster is killed, but still call for every monster.
   -- Basically this divides the number of times the function is called into the number
   -- of monsters and actually completes when all the monsters are killed.  It first
   -- error checks to ensure theres no division by zero (though that shouldn't be
   -- possible).
--   crawl.mpr(killer_type)
   if v_wave_qty == 0 then
      error("division by zero in callback.main_mons_die")
      return
   end
   local fake_num = v_mon_die_call / v_map_spawn_positions
   if fake_num == v_wave_qty then
     crawl.mpr("You have killed all " .. v_wave_qty .. " monsters.")
   else
     return
   end
  end
end

-- This function is called by destination vaults and signals that the portal
-- has been completed.
function arena_milestone(e)
  crawl.mark_milestone("br.enter", "entered an Arena.", true)
end
}}

#####################
# Entry portals
#####################
# Bare-bones portal vault entry

NAME:     stuff_that
TAGS:     allow_dup no_monster_gen
ORIENT:  float
PLACE:    D:1
KFEAT:    O = stone_arch
: setup_arena_portal(_G)
{{
lua_marker('O', props_marker { portal = 1 })
}}
MARKER: M = lua: MonPropsMarker:new {monster_dies_lua_key = callback.entry_mons_die}
MAP
.....
.....
..O..
..M..
.....
ENDMAP

####################
# Destination portals
####################

NAME:     arena_generic_default
TAGS:     arena_portal_default allow_dup
ORIENT:  encompass
: arena_setup(_G)
{{
  local choice_prompt = TriggerableFunction:new{func="callback.arena_choice",
                                                data={difficulty=50} }
  local kill_mons = TriggerableFunction:new{func="callback.dest_mons_die",repeated = true}
  choice_prompt:add_triggerer(DgnTriggerer:new {type="entered_level"})
  kill_mons:add_triggerer(DgnTriggerer:new {type="monster_dies" , target = "any"})
  lua_marker("<", choice_prompt)
  lua_marker("g", props_marker{ monster = 1 })
  lua_marker("g", kill_mons)
}}
SUBST:    g : .
NSUBST:    e = 2:< / *:.
epilogue{{
arena_milestone(_G)
}}
MAP
ccccccccccccccccccccccccccccccccccc
c.......................g.........c
c...e......g..................e...c
c.................................c
c.....................cc..........c
c.......g..............cc..g......c
c....cc.................cc........c
c..cccccc..........g.....cc.......c
c....cc...................ccc.....c
c..........................ccc....c
c.....cc..........................c
c....cccc.........................c
c....cccc.........................c
c.....cc........<..A....c.........c
c......................cc.........c
c...........c.....................c
c...........c.....................c
c.................................c
c.........g............g..........c
c.....ccc....ccc..................c
c......ccc..ccc...................c
c........cccc..................g..c
c.........cc..g....ccccccc........c
c................ccccccc..........c
c.....g.........cc................c
c...............ccccc...g.........c
c...............cc................c
c................ccccccc..........c
c...e.......g......ccccccc....e...c
c.................................c
ccccccccccccccccccccccccccccccccccc
ENDMAP


New code.

Return to Contributions

Who is online

Users browsing this forum: No registered users and 4 guests

cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.