Compare Revisions
Swap
Showing 22 commits
Merge Ancestor: c62400235fb9
22 silverwing b86ed9567583
6 days ago
21 silverwing 5985b0a31815
6 days ago
20 silverwing fe8693a3e7d5
6 days ago
19 silverwing 93cfddc9484c
6 days ago
18 silverwing 1332d630308f
6 days ago
17 silverwing f2f018459fb7
6 days ago
16 silverwing 0c055c1e38c1
6 days ago
15 silverwing a3357855c7d3
6 days ago
14 silverwing 9cf402fecdaf
8 days ago
13 silverwing 79074f9e816e
8 days ago
12 silverwing b1f91fd22b0b
8 days ago
11 silverwing 7da04978fd8a
2 months ago
10 silverwing f64fd360962a
2 months ago
9 silverwing 6e2e4d683a57
2 months ago
8 silverwing 3c6baa05ef09
3 months ago
7 silverwing 0ac34928cadb
3 months ago
6 silverwing e0ab59b58fc1
3 months ago
5 silverwing b16a671170b8
3 months ago
4 silverwing 6318c07fd328
3 months ago
3 silverwing 009ae74f1983
3 months ago
2 silverwing c2e2cb3a6e5d
3 months ago
1 silverwing 3dcee4fa8059
4 months ago
21 files changed:
ending.lua
Show inline comments
 
@@ -50,6 +50,9 @@ function show_ending(theme, image, posit
 
    position: nil or 0 -- center, 1 -- bottom-left
 
  ]]
 
  --frames, process, delay, frame_nr
 
  if instead.tiny then
 
    return
 
  end;
 
  
 
	local scale = diretheme.get_scale();
 
	
 
@@ -72,7 +75,7 @@ function show_ending(theme, image, posit
 
  
 
  if hide_textbg then
 
    D {'textbg'};
 
  else
 
  elseif unscaled_sw ~= 640 or unscaled_sh ~= 480 then
 
    local sw, sh = diretheme.get_screen();
 
    local tx = 24 * scale;
 
    local tw = sw - tx - 24 * scale;
 
@@ -151,24 +154,20 @@ room {
 
		show_ending('main', 'images/endings/tower_left_bg.png');
 
		timer:set(25);
 
    
 
    local animheight = 600 * scale;
 
    if width == 640 and height == 480 then
 
      animheight=360 * scale;
 
    end;
 
    
 
    animation_set({
 
      {type="delay", delay=2000};
 
      {
 
          type="image", 
 
          image=get_sprite('images/endings/tower_left_bg.png'),
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          background = true
 
        };
 
      {
 
          type="anim",
 
          image=get_sprite('images/endings/tower_left_frames.png'),
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale, 
 
          h = 600 * scale, 
 
          h = animheight, 
 
          frames=6,
 
          delay=500,
 
          frame_data = {
 
@@ -576,6 +575,11 @@ room {
 
    local dx = math.floor((width - 960) / 2);
 
    local dy = math.floor((height - 600) / 2);
 
    
 
    local animheight = 600 * scale;
 
    if width == 640 and height == 480 then
 
      animheight=360 * scale;
 
    end;
 
    
 
		show_ending('main', 'images/endings/tower_left_bg.png');
 
    animation_set({
 
        {type="delay", delay=2000};
 
@@ -585,7 +589,7 @@ room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight,
 
          background = true
 
        };
 
        {
 
@@ -594,7 +598,7 @@ room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight,
 
          frames = 15,
 
          delay = 100,
 
          background = true,
 
@@ -622,9 +626,10 @@ room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale, 
 
          h = 600 * scale, 
 
          h = animheight,
 
          frames=6,
 
          delay=500,
 
          background = true,
 
          frame_data = {
 
            {};
 
            {x=0, y=318, w=51, h=16,  dx=444, dy=246};
 
@@ -718,6 +723,11 @@ room {
 
      dy = height - 600;
 
    end;
 
    
 
    local animheight = 600;
 
    if width == 640 and height == 480 then
 
      animheight = 420;
 
    end;
 
    
 
		show_ending('main', 'images/endings/crater_edge/solarscale.png', 1);
 
    animation_set({
 
        {type="delay", delay=2000};
 
@@ -727,7 +737,7 @@ room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight * scale,
 
          background = true
 
        };
 
        {
 
@@ -736,7 +746,7 @@ room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight * scale,
 
          frames = 28,
 
          delay = 125,
 
          background = true,
 
@@ -780,12 +790,55 @@ room {
 
	};
 
}
 

	
 
local function bayer(size)
 
  if size == 1 then
 
    return {
 
      {0}
 
    };
 
  end;
 
  
 
  local u = bayer(size / 2);
 
  local v = 4;
 
  local h = size / 2;
 
  local m = {};
 
  
 
  for j = 1, h do
 
    m[j] = {};
 
    for i = 1, h do
 
      m[j][i] = u[j][i] * v;
 
    end;
 
  end;
 
  
 
  for j = h + 1, size do
 
    m[j] = {};
 
    for i = 1, h do
 
      m[j][i] = u[j - h][i] * v + 3;
 
    end;
 
  end;
 
  
 
  for j = 1, h do
 
    for i = h + 1, size  do
 
      m[j][i] = u[j][i - h] * v + 2;
 
    end;
 
  end;
 
  
 
  for j = h + 1, size do
 
    for i = h + 1, size do
 
      m[j][i] = u[j - h][i - h] * v + 1;
 
    end;
 
  end;
 
  
 
  return m;
 
end;
 

	
 
ending_room {
 
  nam = 'ending_welcome';
 
  theme = 'ending_inside';
 
  themeInit = function(s)
 
    if instead.tiny then
 
      return;
 
    end;
 
    local scale = diretheme.get_scale();
 
    local uwidth, uheight = diretheme.get_screen();
 
    local width, height = diretheme.get_scaled_screen();
 
    local pic_dx = math.floor((width - 960) / 2);
 
    local pic_dy = math.floor((height - 600) / 2);
 
@@ -810,41 +863,122 @@ ending_room {
 
      src = sprite.new('images/endings/pre_space_1.png');
 
      dst = sprite.new('images/endings/pre_space_2.png');
 
    end;
 
    
 
    if width == 640 and height == 480 then
 
      height = 300;
 
    end;
 
    
 
    if pic_dx < 0 or pic_dy < 0 then
 
      local src2 = sprite.new(width, height);
 
      src:copy(-pic_dx, -pic_dy, width, height, src2, 0, 0);
 
      src = src2;
 
    end;
 
    
 
    newsrc = sprite.new(width, height);
 
    src:copy(-pic_dx, -pic_dy, width, height, newsrc, 0, 0);
 
    
 
    -- A tiny performance test: painting 1 - 512 lines, doubling every time. If any of the operations takes more than 10ms -- switch to lo-perf mode
 
    local passed = true;
 
    local test = 1;
 
    while passed and test <= 512 do
 
      local t = instead.ticks();
 
      local tdst = sprite.new(width, test);
 
      tdst:draw(-pic_dx, 0, width, test, newsrc, 0, 0, 1);
 
      if instead.ticks() - t > 10 then
 
        passed = false;
 
      end;
 
      test = test * 2;
 
    end;
 
    
 
    src:copy(0, 0, width, height, newsrc, 0, 0);
 
    
 
    local alpha = 1;
 
    local part = 0;
 
    local partcount = 10;
 
    local partheight = math.floor(600 / partcount);
 
    local partheight;
 
    local parts = {};
 
    local dalpha = 1;
 
    local alternative = false;
 
    local delta = 25;
 
    
 
    -- Now calculate animation speed
 
    -- Expected duration is 125 ms before shifting alpha by 1
 
    if passed then
 
      -- Test passed, can paint the whole sprite in one part
 
      partcount = 1;
 
      partheight = height;
 
    else
 
      -- test failed; Calculate, how fast painting can be done
 
      test = math.max(test / 4, 1); -- Rows in one frame
 
      partcount = math.ceil(height / test);
 
      dalpha = math.floor(partcount / 5);
 
      alpha = dalpha;
 
      partheight = math.floor(height / partcount);
 
    end;  
 
    
 
    if partcount > 25 then
 
      -- test completely failed; Use alternative animation
 
      alternative = true;
 
      alpha = 1;
 
      delta = 125;
 
    else
 
    for i = 1, partcount do
 
      parts[i] = sprite.new(width, partheight);
 
      dst:copy(-pic_dx, (i - 1) * partheight, width, partheight, parts[i], 0, 0);
 
        dst:copy(-pic_dx, (i - 1) * partheight - pic_dy, width, partheight, parts[i], 0, 0);
 
      end;
 
      dst = nil;
 
    end;    
 
    
 
    local process = function(s)
 
      src:copy(-pic_dx, part * partheight, width, partheight, newsrc, 0, pic_dy + part * partheight);
 
    local process;
 
    if alternative then
 
      local map = {};
 
      local m = bayer(16);
 
      for j = 1, 16 do
 
        for i = 1, 16 do
 
          map[i + j * 16 - 16] = m[j][i];
 
        end;
 
      end;      
 
      
 
      dst = dst:scale(scale, scale, false);
 
      
 
      process = function(s)
 
        local i = map[alpha];
 
        while i < height do
 
          if scale == 1 then
 
            dst:copy(-pic_dx, i - pic_dy, width, 1, s.sprite, 0, i);
 
          else
 
            dst:copy(-pic_dy, i * scale - pic_dy, width * scale, scale, s.sprite, 0, i * scale);
 
          end;
 
          i = i + 256;
 
        end;
 
        
 
        alpha = alpha + 1;
 
        if alpha == 257 then
 
          s.process = nil
 
        end;
 
      end;
 
    else
 
      process = function(s)
 
        src:copy(0, part * partheight, width, partheight, newsrc, 0, part * partheight);
 
      
 
      if alpha < 255 then
 
        dst:draw(-pic_dx, part * partheight, width, partheight, newsrc, 0, pic_dy + part * partheight, alpha);
 
          parts[part + 1]:draw(newsrc, 0, part * partheight, alpha);
 
      else
 
        dst:copy(-pic_dx, part * partheight, width, partheight, newsrc, 0, pic_dy + part * partheight);
 
          parts[part + 1]:copy(newsrc, 0, part * partheight);
 
      end;
 
      
 
      part = part + 1
 
      if part >= partcount then
 
        part = 0
 
        alpha = alpha + 2
 
        if alpha > 255 then
 
          s.process = nil
 
          -- TODO End
 
        end;
 
        
 
          part = 0
 
          alpha = alpha + dalpha;
 
          
 
        if scale ~= 1 then
 
          s.sprite = newsrc:scale(scale, scale, false);
 
        else
 
          elseif partcount > 1 then
 
          newsrc:copy(s.sprite, 0, 0);
 
        end;
 
        
 
@@ -853,6 +987,7 @@ ending_room {
 
      
 
      return true;
 
    end;
 
    end;
 

	
 
    if solarscale_stays then
 
      show_ending('tower', 'images/endings/pre_space_1_solarscale.png');
 
@@ -862,12 +997,14 @@ ending_room {
 
    local spr
 
    if scale ~= 1 then
 
      spr = newsrc:scale(scale, scale, false);
 
    elseif partcount == 1 then
 
      spr = newsrc;
 
    else
 
      spr = newsrc:dup();
 
    end;
 
      
 
    animation_set({
 
      {type="image", image=spr, x=dx * scale, y=dy * scale, w=width * scale, h=height * scale, process=process, delay=25, background=true};
 
      {type="image", image=spr, x=dx * scale, y=dy * scale, w=width * scale, h=height * scale, process=process, delay=125, background=true};
 
    });
 
    spr = nil;  -- remove reference
 
    timer:set(25);
 
@@ -888,11 +1025,19 @@ ending_room {
 
  nam = 'ending_solarscale_flight';
 
  theme = 'ending_inside';
 
  themeInit = function(s)
 
    if instead.tiny then
 
      return;
 
    end;
 
    local scale = diretheme.get_scale();
 
    local width, height = diretheme.get_scaled_screen();
 
    local dx = math.floor((width - 960) / 2);
 
    local dy = math.floor((height - 600) / 2);
 
    
 
    local animheight = 600 * scale;
 
    if width == 640 and height == 480 then
 
      animheight=360 * scale;
 
    end;
 
    
 
    show_ending('tower', 'images/endings/solarscale/solarscale_first.png');
 
    animation_set({
 
        {type="delay", delay=2000};
 
@@ -902,7 +1047,7 @@ ending_room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight,
 
          background = true
 
        },
 
        {
 
@@ -911,7 +1056,7 @@ ending_room {
 
          x = dx * scale,
 
          y = dy * scale,
 
          w = 960 * scale,
 
          h = 600 * scale,
 
          h = animheight,
 
          frames = 70,
 
          delay={
 
            150, 150, 150, 150, 150,
 
@@ -1015,6 +1160,9 @@ ending_room {
 
  nam = 'ending_space_1';
 
  theme = 'ending_inside';
 
  themeInit = function(s)
 
    if instead.tiny then
 
      return;
 
    end;
 
    if solarscale_stays then
 
      show_ending('tower', 'images/endings/solarscale/solarscale_first.png');
 
    else
 
@@ -1052,6 +1200,9 @@ room {
 
  noautosave = true;
 
	theme = 'ending_inside';
 
  themeInit = function(s)
 
    if instead.tiny then
 
      return;
 
    end;
 
		if solarscale_stays then
 
      show_ending('tower', 'images/endings/solarscale/solarscale_first.png');
 
    else
 
@@ -1099,6 +1250,24 @@ room {
 
  };
 
};
 

	
 
function credits_text(centered) 
 
  local closed_beta_participants = 'arbtttrn6, De@th K!d, Fehatt, Lesha Pak, Łukasz Sawicki, Niko Flang, Oleg Bosh, Terracon, Андрей Лобанов';
 
  local tab = '<tab>';
 
  if centered then
 
    tab = '';
 
  end;
 
  return translate_now("menu_locale", 'credits_author') .. ': Khaelenmore Thaal^' ..
 
      translate_now("menu_locale", 'credits_art') .. ': Sabra Naikomy^' ..
 
      translate_now("menu_locale", 'credits_music') .. ': Khaelenmore Thaal^' ..
 
      translate_now("menu_locale", 'credits_engine') .. ': INSTEAD ' .. translate_now("menu_locale", 'credits_engine_author') .. ' {$link https://github.com/instead-hub/instead/}^' ..
 
      translate_now("menu_locale", 'credits_sounds') .. ': {$link https://freesound.org/} CC-0^' ..
 
      translate_now("menu_locale", 'credits_fonts') .. ': VileR {$link https://int10h.org/oldschool-pc-fonts/} CC-BY-SA 4.0^^' ..
 
      translate_now("menu_locale", 'credits_closed_beta') .. ': ' .. closed_beta_participants.. '^^'..
 
      translate_now("menu_locale", "credits_translation") .. ':^' ..
 
      tab .. 'Esperanto: arbtttrn6' .. '^^' ..
 
      translate_now("menu_locale", 'credits_thanks');
 
end;
 

	
 
room {
 
	nam = 'ending_credits';
 
	disp = translate("ending_locale", 'thank_you_for_playing');
 
@@ -1149,14 +1318,7 @@ room {
 
    fmt.para = true;
 
  end;
 
	decor = function(s)
 
		return translate_now("menu_locale", 'credits_author') .. ': Khaelenmore Thaal^' ..
 
			translate_now("menu_locale", 'credits_art') .. ': Sabra Naikomy^' ..
 
			translate_now("menu_locale", 'credits_music') .. ': Khaelenmore Thaal^' ..
 
			translate_now("menu_locale", 'credits_engine') .. ': INSTEAD ' .. translate_now("menu_locale", 'credits_engine_author') .. ' {$link https://github.com/instead-hub/instead/}^' ..
 
			translate_now("menu_locale", 'credits_sounds') .. ': {$link https://freesound.org/} CC-0^' ..
 
			translate_now("menu_locale", 'credits_fonts') .. ': VileR {$link https://int10h.org/oldschool-pc-fonts/} CC-BY-SA 4.0^^' ..
 
      translate_now("menu_locale", 'credits_closed_beta') .. ': ' .. closed_beta_participants.. '^^'..
 
			translate_now("menu_locale", 'credits_thanks');
 
    return credits_text(true);
 
	end;
 
  way = {
 
    path {translate("ending_locale", 'show_menu'), 'reset'};
game_theme.lua
Show inline comments
 
new file 100644
 
if instead.tiny then
 
  -- Stub the theme if instead.tiny is present
 
  function set_theme(t) end;
 
  return;
 
end;
 

	
 
function set_theme(t)  
 
  -- resetting defaults
 
  fmt.para = true;
 
  std.scene_delim = '^^';
 
  theme.set('snd.click', 'sound/click.wav');
 
  
 
  D {'settings_open'};
 

	
 
  local tname, tcolor_text, tcolor_link, tcolor_alink;
 

	
 
  local width, height = diretheme.get_screen();
 
  
 
  -- 640x480 is the base size
 
  local scale = get_graphic_scale();
 
  local max_scale_x = math.floor(width / 640);
 
  local max_scale_y = math.floor(height / 480);
 
  -- Fail-safe for too small screen
 
  if max_scale_x < 1 then max_scale_x = 1; end;
 
  if max_scale_y < 1 then max_scale_y = 1; end;
 
    
 
  local max_scale = math.min(max_scale_x, max_scale_y);
 
  
 
  if scale > max_scale then
 
    scale = max_scale;
 
    prefs.graphic_scale = false;
 
  end;
 
  
 
  local cursor_scale = get_cursor_scale();
 
  if cursor_scale > 2 * max_scale then
 
    cursor_scale = 2 * max_scale;
 
  end;
 
  
 
  local font_scale = get_font_scale() / 100;
 
  if font_scale > 2 * scale then
 
    font_scale = scale;
 
    prefs.font_scale = font_scale * 100
 
  end;
 
  
 
  local inv_scale = get_inventory_font_scale() / 100;
 
  
 
  local text_align = 'justify';
 
  if prefs.text_alignment == 2 then
 
    text_align = 'left';
 
  end;
 
  
 
  local hpadding, vpadding = 0, 0;
 
  if prefs.text_padding == 2 then
 
    hpadding, vpadding = 24, 12;
 
  end;
 
  
 
  local interval = '1.0';
 
  if prefs.text_interval == 2 then
 
    interval = '1.5';
 
  end;
 
  
 
  local canvas = diretheme.set_canvas({rmax = 2, rmin = 1});
 
  diretheme.set_scale(scale);
 

	
 
  local scaled_width, scaled_height = diretheme.get_scaled_screen();
 

	
 
  current_theme = t;
 
  local tvariant = 0;
 
  if t:match("_bw$") then
 
    t = t:gsub("_bw$", "");
 
    tvariant = 1;
 
  end;
 
  
 
  -- Most text area settings are per-theme
 
  local text_area = {};
 
  
 
  -- Hide inventory by default
 
  local inventory = {
 
    rect = {
 
      lx = width + 1,
 
      y = 0,
 
      wmax = 1,
 
      wmin = 1,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    }
 
  };
 
  
 
  -- And image
 
  local image_area = {
 
    rect = {
 
      lx = width + 1,
 
      ax = 1,
 
      wmax = 584,
 
      hmax = 258,
 
      wmin = 584,
 
      hmin = 258
 
    };
 
    bg = {};
 
    mode = 'float';
 
  };
 
  
 
  -- Preset colors
 
  local theme_directory = nil;
 
  local text_color, link_color, alink_color;
 
  local inv_color, inv_link, inv_alink;
 
  if t == 'terminal' or t == 'black' then
 
    diretheme.set_canvas({
 
      lx = 0, ty = 0, rx = 0, by = 0
 
    });
 
    text_color = '#D0FFFF';
 
    link_color = '#ABDECE';
 
    alink_color = '#9BCEBE';  
 
    theme_directory = 'images/tower_theme';
 
    
 
    text_area.text = {
 
      font = 'fonts/vga.ttf',
 
      color = text_color,
 
      link = link_color,
 
      alink = alink_color,
 
      size = 16
 
    };
 
  else
 
    canvas = diretheme.set_canvas({
 
      lx = 0, ty = 0, rx = 0, by = 0, rmin = 1.2, rmax = 1.8
 
    });
 
    
 
    if t == 'menu' or t == 'main' or t == 'ending_outside' or t == 'credits_outside' then
 
      inv_color = '#403426';
 
      inv_link = '#403426';
 
      inv_alink = '#61503A';
 
      
 
      if tvariant == 0 then
 
        text_color = '#000504';
 
        link_color = '#80382A';
 
        alink_color = '#80605D';
 
        theme_directory = 'images/main_theme';
 
      else
 
        text_color = '#040404';
 
        link_color = '#434343';
 
        alink_color = '#585858';
 
        theme_directory = 'images/bwm_theme';
 
      end;
 
      
 
      text_area.uppos = {ax=1, x = 12, y = 0, ay=-1};
 
      text_area.downpos = {ax=1, x = 12, y = 0, ay=1};
 
      text_area.bg = {
 
        image = theme_directory .. '/text_bg.png',
 
        options = {
 
          mode = 6,
 
          drawmethod = 2,
 
          slice = {
 
            x = 9,
 
            y = 6,
 
            w = 96,
 
            h = 96
 
          },
 
          scale = true
 
        }
 
      };
 
    elseif t == 'tower' or t == 'ending_inside' or t == 'credits_inside' then
 
      inv_color = '#6b9e8e';
 
      inv_link = '#9bcebe';
 
      inv_alink = '#Bbeede';
 
      
 
      if tvariant == 0 then
 
        text_color = '#93DDCD';
 
        link_color = '#E0F0EA';
 
        alink_color = '#FFFFFF';
 
        theme_directory = 'images/tower_theme';
 
      else
 
        text_color = '#ADADAD';
 
        link_color = '#CDCDCD';
 
        alink_color = '#BDBDBD';
 
        theme_directory = 'images/bwt_theme';
 
      end;
 
      
 
      text_area.uppos = {ax=1, x = 16, y = 0, ay=-1};
 
      text_area.downpos = {ax=1, x = 16, y = 0, ay=1};
 
      text_area.bg = {
 
        image = theme_directory .. '/text_bg.png',
 
        options = {
 
          mode = 6,
 
          drawmethod = 2,
 
          slice = {
 
            x = 6,
 
            y = 6,
 
            w = 128,
 
            h = 128
 
          },
 
          scale = true
 
        }
 
      };
 
    end;
 
    
 
    if t == 'ending_inside' or t == 'ending_outside' then
 
      canvas = diretheme.set_canvas({
 
        lx = 0, ty = 0, rx = 0, by = 0
 
      });
 
    end;
 
  
 
    text_area.align = text_align;
 
    text_area.height = interval;
 
    text_area.scaleui = true;
 
    text_area.up = theme_directory .. '/arrow_up.png';
 
    text_area.down = theme_directory .. '/arrow_down.png';
 
    text_area.text = {
 
      font = 'fonts/{sans,sans-b,sans-i,sans-bi}.ttf',
 
      color = text_color,
 
      link = link_color,
 
      alink = alink_color,
 
      size = math.floor(16 * font_scale),
 
      noscale = true
 
    };
 
    
 
    inventory.up = theme_directory .. '/arrow_up.png';
 
    inventory.down = theme_directory .. '/arrow_down.png';
 
    inventory.uppos = {ax=1, x = 8, y = 0, ay=-1};
 
    inventory.downpos = {ax=1, x = 8, y = 0, ay=1};
 
    inventory.text = {
 
      color = inv_color,
 
      link = inv_link,
 
      alink = inv_alink,
 
      size = math.floor(16 * inv_scale),
 
      noscale = true
 
    };
 
    inventory.height = interval;
 
  end;
 
  
 
  if t == 'main' or t == 'tower' then
 
    std.scene_delim = '^' .. fmt.c(fmt.img(theme_directory .. '/line_separator.png')) .. '^'
 
  end;
 
  
 
 -- And menu, but need theme_directory to have proper button image
 
  local menu = {
 
    button = {
 
      image = theme_directory ..'/menu.png';
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
    buttonrect = {
 
      x = width + 1,
 
      y = 0,
 
      w = 50,
 
      h = 16
 
    };
 
    text = {
 
      size = math.floor(15 * font_scale),
 
      noscale = true
 
    },
 
    height = interval,
 
    scaleui = true;
 
  };
 
  
 
  local cursor, active_cursor = 'images/cursor.png', 'images/cursor_active.png';
 
  if tvariant == 1 then
 
    cursor, active_cursor = 'images/cursor_bw.png', 'images/cursor_active_bw.png'
 
  end;
 
  
 
  diretheme.set_cursor({
 
    x = 2,
 
    y = 2,
 
    normal = cursor,
 
    active = active_cursor,
 
    scale = true;
 
    scalefactor = cursor_scale;
 
  });
 
  
 
  if t == 'menu' then
 
    diretheme.set_background('#89C6AE', 255, 'images/main_theme/background.png', {mode=2, scale=true});
 
    local padding_delta = -10;
 
    if height < 520 * scale then
 
      padding_delta = -95;
 
    end;
 
    
 
    text_area.rect = {
 
      ty = padding_delta + vpadding,
 
      wmax = 538 - hpadding * 2,
 
      hmax = 322 - vpadding * 2,
 
      ay = 0
 
    };
 
    text_area.text = text_area.text;
 
    text_area.uppos = {ax=1, x = 15, y = 65, ay=-1};
 
    text_area.downpos = {ax=1, x = 15, y = -20, ay=1};
 
    text_area.contentrect = {wf=prefs.text_width / 100};
 
    text_area.bg = '';
 
  elseif t == 'main' then
 
    diretheme.set_background('#89C6AE', 255, 'images/main_theme/background.png', {mode=2, scale = true});
 
    
 
    local image_bg = 'images/main_theme/picture_bg.png';
 
    if canvas.w < 800 * scale then
 
      image_bg = 'images/main_theme/picture_bg_small.png';
 
    end;
 
    
 
    image_area.leftof = 'inv';
 
    image_area.topof = 'text';
 
    image_area.rect = {
 
      ax = 0,
 
      wmax = 584,
 
      hmax = 265,
 
      wmin = 584,
 
      hmin = 265
 
    };
 
    image_area.contentrect = {
 
      ax = 0,
 
      ay = -1,
 
      ty = 11,
 
      wmin = 320,
 
      hmin = 240,
 
      wmax = 320,
 
      hmax = 240
 
    };
 
    image_area.bg = {
 
      image = image_bg,
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
    
 
    text_area.leftof = 'inv';
 
    text_area.rect = {
 
      lx = 7,
 
      ty = 266,
 
      rx = 8,
 
      by = 2,
 
      ax = 1,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 21 + hpadding,
 
      rx = 21 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf=prefs.text_width / 100
 
    };
 
    
 
    inventory.rect = {
 
      rx = 2,
 
      y = 2,
 
      wmax = 208,
 
      wmin = 208,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    };
 
    inventory.contentrect = {
 
      lx = 25,
 
      rx = 25,
 
      ty = 40,
 
      by = 40
 
    };
 
    inventory.bg = {
 
      image = function(tpx, rect)
 
        local top = pixels.new('images/main_theme/inv_top.png'):scale(scale, scale, false);
 
        local mid = pixels.new('images/main_theme/inv_mid.png'):scale(scale, scale, false);
 
        local bottom = pixels.new('images/main_theme/inv_bottom.png'):scale(scale, scale, false);
 
        
 
        dire_draw_tile(mid, tpx, { x=rect.x + 11 * scale, y=rect.y + 10 * scale, w=rect.w - 22 * scale, h = rect.h - 20 * scale}, {});
 
        dire_draw_center(top, tpx, {x=rect.x, y=rect.y, w=208 * scale, h=60 * scale});
 
        dire_draw_center(bottom, tpx, {x=rect.x, y=rect.y + rect.h - 60 * scale, w=208 * scale, h=60 * scale});
 
      end,
 
      options = {scale = true}
 
    };
 

	
 
    menu.buttonrect = {
 
      wmin = 50,
 
      wmax = 50,
 
      hmin = 16,
 
      hmax = 16,
 
      by = 12,
 
      ay = 1,
 
      rx = 81,
 
      ax = 1
 
    };
 

	
 
    D {
 
      'settings_open', 
 
      'raw', 
 
      x=canvas.x + canvas.w - 33 * scale, 
 
      y=canvas.y + canvas.h - 30 * scale, 
 
      xc=0,
 
      yc=0,
 
      w=24 * scale,
 
      h=22 * scale,
 
      click=true,
 
    };
 
  elseif t == 'tower' then
 
    diretheme.set_background('#181E37', 255, 'images/tower_theme/background.png', {mode=2, scale=true});
 
    
 
    local image_bg = 'images/tower_theme/picture_bg.png';
 
    if canvas.w < 800 * scale then
 
      image_bg = 'images/tower_theme/picture_bg_small.png';
 
    end;
 
    
 
    image_area.leftof='inv';
 
    image_area.topof='text';
 
    image_area.rect = {
 
      ax = 0,
 
      maxw = 584,
 
      maxh = 258,
 
      minw = 584,
 
      minh = 258
 
    };
 
    image_area.contentrect = {
 
      ax = 0,
 
      ay = -1,
 
      ty = 12,
 
      wmin = 320,
 
      hmin = 240,
 
      wmax = 320,
 
      hmax = 240
 
    };
 
    image_area.bg = {
 
      image = image_bg,
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
    
 
    text_area.leftof = 'inv';
 
    text_area.rect = {
 
      lx = 7,
 
      ty = 266,
 
      rx = 8,
 
      by = 2,
 
      ax = 1,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 21 + hpadding,
 
      rx = 21 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
  
 
    inventory.rect = {
 
      rx = 2,
 
      y = 2,
 
      wmax = 208,
 
      wmin = 208,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    };
 
    inventory.contentrect = {
 
      lx = 12,
 
      rx = 16,
 
      ty = 10,
 
      by = 40
 
    };
 
    inventory.bg = {
 
      image = function(tpx, rect)
 
        local bg = pixels.new('images/tower_theme/inv_bg.png'):scale(scale, scale, false);
 
        local icon = pixels.new('images/tower_theme/settings.png'):scale(scale, scale, false);
 
        
 
        local tbg, tbg_slice = dire_slice_9(bg, {
 
          x = 7 * scale,
 
          y = 11 * scale,
 
          w = 194 * scale,
 
          h = 38 * scale
 
        })
 
        
 
        dire_draw_9(tbg, tpx, rect, {parts=tbg_slice});
 
        
 
        dire_draw_center(icon, tpx, {x = rect.x + rect.w - 33 * scale, y = rect.y + rect.h - 35 * scale, w = 24 * scale, h = 22 * scale});
 
      end
 
    };
 
    
 
    menu.buttonrect = {
 
      wmin = 50,
 
      wmax = 50,
 
      hmin = 16,
 
      hmax = 16,
 
      by = 20,
 
      ay = 1,
 
      rx = 81,
 
      ax = 1
 
    };
 
    
 
    D {
 
      'settings_open', 
 
      'raw',
 
      x=canvas.x + canvas.w - 35 * scale, 
 
      y=canvas.y + canvas.h - 37 * scale,
 
      xc=0,
 
      yc=0,
 
      w=24 * scale,
 
      h=22 * scale,
 
      click=true
 
    };
 
  elseif t == 'terminal' then
 
    theme.set('snd.click', '');
 
    
 
    local upshift = 0;
 
    if height / scale < 540 then
 
      upshift = 19;
 
    end;
 
    
 
    local px = pixels.new(scaled_width, scaled_height);
 
    local tiles_src = pixels.new('images/terminal/table_tiles.png');
 
    local backdrop_src = pixels.new('images/terminal/backdrop.png');
 
    
 
    local tabletop = scaled_height - 422 - upshift
 
    if tabletop < 335 - upshift then
 
      tabletop = 335 - upshift;
 
    end;
 
    
 
    dire_draw_tile(tiles_src, px, {
 
      w = scaled_width,
 
      h = 422,
 
      x = 0,
 
      y = tabletop
 
    }, {xoffset = -1, yoffxet = -1});
 

	
 
    dire_draw_tile(backdrop_src, px, {
 
      w = scaled_width,
 
      h = tabletop,
 
      x = 0,
 
      y = 0
 
    }, {xoffset = -1, yoffset = -2});
 
    
 
    diretheme.set_background('#181E37', 255, px, {mode=5});
 
    
 
    local ty
 
    if here().ticks ~= nil and here().ticks < 128 and here().graphical then
 
      ty = 72
 
    else
 
      ty = 2
 
    end;    
 

	
 
    text_area.rect = {
 
      ty = 0 - upshift,
 
      by = 189,
 
      wmin = 808,
 
      hmin = 568,
 
      wmax = 808,
 
      hmax = 568,
 
      ax = 0,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      wmax = 569,
 
      hmax = 390,
 
      ty = ty,
 
      ax = 0,
 
      ay = -2,
 
      dx = -16
 
    };
 
    text_area.bg = {
 
      image = 'images/terminal/bg_text.png',
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
  elseif t == 'ending_outside' then
 
    diretheme.set_background('#89C6AE', 255, theme_directory .. '/background.png', {mode=2, scale=true});
 
    
 
    if scaled_width == 640 and scaled_height == 480 then
 
      text_area.rect = {
 
        lx = 0,
 
        rx = 0,
 
        wmax = 640,
 
        hmax = 180,
 
        by = 0,
 
        ay = 1
 
      };
 
    else
 
      text_area.rect = {
 
        lx = 24,
 
        rx = 24,
 
        wmax = 600,
 
        hmax = 160,
 
        by = 24,
 
        ay = 1
 
      };
 
    end;
 
    
 
    text_area.contentrect = {
 
      lx = 18 + hpadding,
 
      rx = 18 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
  elseif t == 'ending_inside' then
 
    diretheme.set_background('#181E37', 255, theme_directory .. '/background.png', {mode=2});
 
    
 
     if scaled_width == 640 and scaled_height == 480 then
 
      text_area.rect = {
 
        lx = 0,
 
        rx = 0,
 
        wmax = 640,
 
        hmax = 180,
 
        by = 0,
 
        ay = 1
 
      };
 
    else
 
      text_area.rect = {
 
        lx = 24,
 
        rx = 24,
 
        wmax = 600,
 
        hmax = 160,
 
        by = 24,
 
        ay = 1
 
      };
 
    end;
 
    
 
    text_area.contentrect = {
 
      lx = 18 + hpadding,
 
      rx = 18 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
  elseif t == 'credits_outside' or t == 'credits_inside' then
 
    -- text scaled on top of picture
 
    text_area.rect = {
 
      wmax = 960,
 
      hmax = 600
 
    };
 
    text_area.bg = {};
 
    text_area.align = 'center';
 
    text_area.content_rect = {
 
      wf = prefs.text_width / 100
 
    };
 
  elseif t == 'black' then    
 
    fmt.para = false;
 
    diretheme.set_background('#000000', 255);
 
    
 
    text_area.rect = {
 
      wmax = 600,
 
      hmax = 400,
 
      ax = 0,
 
      ay = 0
 
    };
 
    text_area.align = 'center';
 
  end;
 
  
 
  diretheme.set_image_area(image_area, true);
 
  diretheme.set_text_area(text_area, true);
 
  diretheme.set_inventory_area(inventory, true);
 
  
 
  diretheme.set_menu(menu, true);
 
end;
 
\ No newline at end of file
images/terminal/keyboard_shift.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/about.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/back.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/load.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/quit.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/save.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/settings.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/start.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
images/title/tutorial.eo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
lib/decor.lua
Show inline comments
 
@@ -199,6 +199,8 @@ function img:render(v)
 
	if v.background then
 
		if v.frames and v.fw and v.fh then
 
			v.sprite:copy(v.fx, v.fy, v.fw, v.fh, sprite.scr(), v.dx - v.xc, v.dy - v.yc)
 
    elseif v.w and v.h then
 
      v.sprite:copy(0, 0, v.w, v.h, sprite.scr(), v.x - v.xc, v.y - v.yc)
 
		else
 
			v.sprite:copy(sprite.scr(), v.x - v.xc, v.y - v.yc)
 
		end
 
@@ -1158,6 +1160,7 @@ function decor:render()
 
		self[v.type]:render(v)
 
	end
 
end
 

	
 
std.mod_init(function(s)
 
	local oldrender = sprite.render_callback()
 
	sprite.render_callback(
lib/locale.lua
Show inline comments
 
@@ -8,6 +8,7 @@ local lang_map = {
 

	
 
local lang_list = {
 
  {'en', 'English'},
 
  {'eo', 'Esperanto'},
 
  {'ru', 'Русский'},
 
}
 

	
lib/smoke.lua
Show inline comments
 
@@ -44,6 +44,10 @@ function coords(x)
 
end;
 

	
 
function enableSmoke(image, position)
 
  if prefs.simplified_animation then
 
    return
 
  end;
 
  
 
  local bg = smokebg(image, position);
 
  
 
  local w, h = diretheme.get_screen();
lib/tiny/stubs.lua
Show inline comments
 
new file 100644
 
--[[
 
  Stub anything to make the game work with tiny instead/plainstead
 
]]
 

	
 
require "click"
 

	
 
-- core instead
 
if std.rawget(_G, "LANG") == nil then
 
  std.rawset(_G, "LANG", 'en');
 
end;
 

	
 
if not std.savepath then
 
  std.savepath = instead.savepath
 
end;
 

	
 
if not instead.autosave then
 
  instead.autosave = function() end;
 
end;
 

	
 
-- lib/fading
 
declare {
 
  fading = {};
 
}
 

	
 
-- lib/smoke
 
function enableSmoke(image, position)
 
end;
 
  
 
-- lib/decor
 
function D(n)
 
  
 
end;
 

	
 
-- diretheme
 
diretheme = obj {
 
  nam = '@diretheme',
 
  get_screen = function() return 800,600; end;
 
  get_scaled_screen = function() return 800,600; end;
 
  get_scale = function() return 1; end;
 
  get_inches = function() return 14; end;
 
  set_theme = function() end;
 
}
lib/utils.lua
Show inline comments
 
@@ -200,7 +200,17 @@ function split(text, delim)
 
    return result
 
end;
 

	
 
function scale_frame_data(fd)
 
local max = math.max;
 

	
 
function cut_height(h, dy, max_height)
 
  if max_height and h + dy > max_height then
 
    return max(h - (h + dy - max_height), 0);
 
  else
 
    return h;
 
  end;
 
end;
 

	
 
function scale_frame_data(fd, fw, fh)
 
  local scale = diretheme.get_scale();
 
  for _, frame in ipairs(fd) do
 
    if frame['x'] then
 
@@ -213,7 +223,7 @@ function scale_frame_data(fd)
 
      frame['w'] = frame['w'] * scale;
 
    end;
 
    if frame['h'] then
 
      frame['h'] = frame['h'] * scale;
 
      frame['h'] = cut_height(frame['h'] * scale, frame['dy'], fh);
 
    end;
 
    if frame['dx'] then
 
      frame['dx'] = frame['dx'] * scale;
 
@@ -225,3 +235,22 @@ function scale_frame_data(fd)
 
  
 
  return fd;
 
end;
 

	
 
function cat_list(list, sep)
 
  local rval;
 

	
 
  if sep == nil then
 
    sep = '';
 
  end;
 

	
 
  for i, v in ipairs(list) do
 
    if rval then
 
      rval = rval .. sep .. v;
 
    else
 
      rval = v;
 
    end;
 
  end;
 

	
 
  return rval;
 
end;
 

	
main3.lua
Show inline comments
 
@@ -3,23 +3,28 @@
 
-- $Author: Khaelenmore Thaal & Sabra Naikomy$
 
-- $Info: A tiny illustrated text adventure$
 

	
 
if instead.tiny then
 
  require "lib/tiny/stubs"
 
end;
 
require "lib/link"
 
require "noinv"
 
require 'snd'
 
require 'keys'
 
require "timer"
 
require "theme"
 
if not instead.tiny then
 
require "lib/decor"
 
end;
 
require "prefs"
 
if not instead.tiny then
 
  require "lib/dire/theme"
 
end;
 
require 'lib/utils'
 
require "lib/dire/theme"
 
require "lib/locale"
 
if not instead.tiny then
 
require "lib/fading"
 
require "lib/smoke"
 

	
 
declare({
 
    closed_beta_participants = 'arbtttrn6, De@th K!d, Fehatt, Lesha Pak, Łukasz Sawicki, Niko Flang, Oleg Bosh, Terracon, Андрей Лобанов';
 
})
 
end
 

	
 
fmt.quotes = false;
 
fmt.filter = function(r)
 
@@ -29,6 +34,8 @@ fmt.filter = function(r)
 
        r = r:gsub('"([^"]*)"', translate_now("common_locale", 'quotes'));
 
    end;
 
    
 
    r = r:gsub('<tab>', fmt.nb('    '));
 
    
 
    return r;
 
end;
 

	
 
@@ -119,31 +126,32 @@ std.phrase_show = false;
 

	
 
declare {
 
	current_theme = false,
 
	sounds = {
 
		['bubble'] = snd.new('sound/bubble.ogg');
 
		['potion'] = snd.new('sound/potion.ogg');
 
		['error_beep'] = snd.new('sound/error_beep.ogg');
 
		['long_beep'] = snd.new('sound/long_beep.ogg'); 
 
		['short_beep'] = snd.new('sound/short_beep.ogg'); 
 
		['note_c'] = snd.new('sound/c.ogg');
 
		['note_d'] = snd.new('sound/d.ogg');
 
		['note_e'] = snd.new('sound/e.ogg');
 
		['note_f'] = snd.new('sound/f.ogg');
 
		['note_g'] = snd.new('sound/g.ogg');
 
		['note_a'] = snd.new('sound/a.ogg');
 
		['note_b'] = snd.new('sound/b.ogg');
 
		['wind_gust_1'] = snd.new('sound/wind_blow_1.ogg');
 
		['wind_gust_2'] = snd.new('sound/wind_blow_2.ogg');
 
		['wind_gust_3'] = snd.new('sound/wind_blow_3.ogg');
 
		['open_door'] = snd.new('sound/open_door.ogg');
 
		['pc_startup'] = snd.new('sound/pc_startup.ogg');
 
		['keyboard1'] = snd.new('sound/keyboard1.ogg');
 
		['keyboard2'] = snd.new('sound/keyboard2.ogg');
 
		['keyboard3'] = snd.new('sound/keyboard3.ogg');
 
		['keyboard4'] = snd.new('sound/keyboard4.ogg');
 
		['keyboard5'] = snd.new('sound/keyboard5.ogg');
 
    ['irradiated'] = snd.new('sound/irradiated.ogg');
 
	},
 
  sound_names = {
 
    ['bubble'] = 'sound/bubble.ogg';
 
    ['potion'] = 'sound/potion.ogg';
 
    ['error_beep'] = 'sound/error_beep.ogg';
 
    ['long_beep'] = 'sound/long_beep.ogg';
 
    ['short_beep'] = 'sound/short_beep.ogg';
 
    ['note_c'] = 'sound/c.ogg';
 
    ['note_d'] = 'sound/d.ogg';
 
    ['note_e'] = 'sound/e.ogg';
 
    ['note_f'] = 'sound/f.ogg';
 
    ['note_g'] = 'sound/g.ogg';
 
    ['note_a'] = 'sound/a.ogg';
 
    ['note_b'] = 'sound/b.ogg';
 
    ['wind_gust_1'] = 'sound/wind_blow_1.ogg';
 
    ['wind_gust_2'] = 'sound/wind_blow_2.ogg';
 
    ['wind_gust_3'] = 'sound/wind_blow_3.ogg';
 
    ['open_door'] = 'sound/open_door.ogg';
 
    ['pc_startup'] = 'sound/pc_startup.ogg';
 
    ['keyboard1'] = 'sound/keyboard1.ogg';
 
    ['keyboard2'] = 'sound/keyboard2.ogg';
 
    ['keyboard3'] = 'sound/keyboard3.ogg';
 
    ['keyboard4'] = 'sound/keyboard4.ogg';
 
    ['keyboard5'] = 'sound/keyboard5.ogg';
 
    ['irradiated'] = 'sound/irradiated.ogg';
 
  };
 
  sounds = {},
 
	gusts = {
 
		'wind_gust_1';
 
		'wind_gust_2';
 
@@ -158,6 +166,42 @@ global {
 
  in_game = false,
 
}
 

	
 
function load_sounds(...)
 
  for i = 1, #arg do
 
    local s = arg[i];
 
    if not sounds[s] then
 
      sounds[s] = snd.new(sound_names[s]);
 
    end;
 
  end;
 
end;
 

	
 
if instead.tiny then
 
  function DSA () end;
 
  function DS () end;
 
else
 
  function DSA (data)
 
    local scale = diretheme.get_scale();
 
    data[3] = get_sprite(data[3]);
 
    data['x'] = data['x'] * scale;
 
    data['y'] = data['y'] * scale;
 
    data['w'] = data['w'] * scale;
 
    data['h'] = data['h'] * scale;
 
    D(data);
 
  end;
 

	
 
  function DS (data)
 
    local scale = diretheme.get_scale();
 
    data[3] = get_sprite(data[3], 'fuchsia');
 
    data['background'] = true;
 
    data['x'] = data['x'] * scale;
 
    data['y'] = data['y'] * scale;
 
    data['w'] = data['w'] * scale;
 
    data['h'] = data['h'] * scale;
 
    D(data);
 
  end;
 
end;
 

	
 
include "game_theme"
 
include "tutorial"
 
include "computer_parts"
 
include "solarscale"
 
@@ -165,51 +209,12 @@ include "tower"
 
include "forest"
 
include "ending"
 

	
 
function get_sprite(path, transparent)
 
	local s = diretheme.get_scale();
 
	
 
	if not menu_sprites[path] then
 
    if s ~= 1 then
 
      menu_sprites[path] = pixels.new(path):scale(s, s):sprite();
 
if instead.tiny then
 
  include "tiny_menu"
 
    else
 
      menu_sprites[path] = sprite.new(path);
 
    end;
 
    if transparent then
 
      menu_sprites[path]:colorkey(transparent);
 
    end;
 
	end;
 
	
 
	return menu_sprites[path];
 
end;
 

	
 
function get_button(button_name)
 
  local path = 'images/title/' .. button_name .. '.' .. get_language() .. '.png';
 
  if menu_sprites[path] then
 
    return menu_sprites[path];
 
  include "menu"
 
  end;
 
  
 
  local sprite = nil;
 
  
 
  --Check if file exists(only used here, so not moved to function)
 
  local f = io.open(path, 'r');  
 
  if f then
 
    f:close();
 
    sprite = get_sprite(path, 'fuchsia')
 
  end;
 
  
 
  if not sprite then
 
    local path = 'images/title/' .. button_name .. '.' .. get_default_language() .. '.png';
 
    sprite = get_sprite(path, 'fuchsia');
 
  end;
 
  return sprite
 
end;
 

	
 
function stop_menu()
 
	D {'bgframe'};
 
	D {'background'};
 
	D {'bganim'};
 
	D {'title'};
 
end;
 

	
 
game.ondecor = function(s, name, press, x, y, btn)
 
	if press then
 
@@ -249,909 +254,6 @@ game.afterwalk = function()
 
  last_location = here().nam;
 
end;
 

	
 
function cat_list(list, sep)
 
	local rval;
 

	
 
	if sep == nil then
 
		sep = '';
 
	end;
 

	
 
	for i, v in ipairs(list) do
 
		if rval then
 
			rval = rval .. sep .. v;
 
		else
 
			rval = v;
 
		end;
 
	end;
 

	
 
	return rval;
 
end;
 

	
 
function init_menu(with_text, buttons)
 
  local scale = diretheme.get_scale();
 
	
 
	local w = 960 * scale;
 
	local h = 600 * scale;
 
	local sw, sh = diretheme.get_screen();
 
	local ox = math.floor((sw - w) / 2);
 
	local oy = math.floor((sh - h) / 2)
 
	
 
	local button_dy = 0;
 
	local button_dx = 0;
 
	if sh < 520 * scale then
 
		button_dy = -50;
 
		button_dx = 20;
 
	end;
 
	
 
	local btop = 515;
 
	
 
	if #buttons == 5 then
 
		btop = 435;
 
	end;
 
	
 
	if sh <= 560 * scale then
 
		btop = btop - 20;
 
	end;
 
	
 
	if #buttons == 1 then
 
		D {
 
			buttons[1], 
 
			'img', 
 
      get_button(buttons[1]),
 
			x=ox + 398 * scale, 
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
	else
 
		D {
 
			buttons[1], 
 
			'img', 
 
			get_button(buttons[1]),
 
			x=ox + (157 + button_dx) * scale, 
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
		D {
 
			buttons[2], 
 
			'img', 
 
			get_button(buttons[2]),
 
			x=ox + 398 * scale,
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
		D {
 
			buttons[3], 
 
			'img', 
 
			get_button(buttons[3]),
 
			x=ox + (639 - button_dx) * scale,
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
	end;
 
	
 
	if #buttons == 5 then
 
		btop = btop + 80
 
		D {
 
			buttons[4], 
 
			'img', 
 
			get_button(buttons[4]),
 
			x=ox + (278 + button_dx / 2) * scale, 
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
		
 
		D {
 
			buttons[5], 
 
			'img', 
 
			get_button(buttons[5]),
 
			x=ox + (519 - button_dx / 2) * scale, 
 
			y=oy + (btop + button_dy) * scale,
 
			click=true,
 
      background=true,
 
      z=3
 
		};
 
	end;
 
	
 
  if sw > w or sh > h then
 
    local bgframe_pixels = pixels.new('images/main_theme/text_bg.png');
 
    local tbg, tbg_slice;
 
    tbg, tbg_slice = dire_slice_9(bgframe_pixels, {
 
      x = 9,
 
      y = 6,
 
      w = 96,
 
      h = 96
 
    });
 
    
 
    local bgframe = pixels.new(978, 612);
 
    dire_draw_9(tbg, bgframe, {x = 0; y = 0; w = 978; h = 612}, {parts=tbg_slice});
 
    D {'bgframe', 'img', bgframe:scale(scale, scale):sprite(), x=ox - 9 * scale, y=oy - 6 * scale, w=w + 18 * scale, h=h + 12 * scale, background=true, z=5};
 
end;
 
  
 
  if not D('background') then
 
    D {'background', 'img', get_sprite('images/title/bg.png'), x=ox, y=oy, z=4, background=true};
 
  end;
 

	
 
	if not D('bganim') then
 
		D {'bganim', 'img', get_sprite('images/title/bg_frames.png'), x=ox, y=oy, w=w, h=h, frames=16, z=3, delay=200, animated=true, background=true, frame_data=scale_frame_data({
 
       {},
 
       {x=0, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=906, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=906, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=906, w=960, h=302, dx=0, dy=0}
 
    })};
 
	end;
 
	if not D('title') then
 
		D {'title', 'img', get_sprite('images/title/title.png', 'fuchsia'), x=ox + 230 * scale, y=oy + 88  * scale, z=2, background=true};
 
	end;
 
	
 
	if with_text then
 
		D {'textbg', 'img', get_sprite('images/title/text_background.png', 'fuchsia'), x=ox + 180 * scale, z=1, y=oy + (126 + button_dy) * scale, background=true};
 
		if sh < 520 * scale then
 
			D {'title'}
 
		else
 
			D('title').y = oy + 40 * scale;
 
		end;
 
	else
 
		D('title').y = oy + 88 * scale;
 
	end;
 
	
 
	timer:set(25);
 
end;
 

	
 
room {
 
	nam = 'main';
 
	notitle = true;
 
	nosave = true;
 
	noautosave = true;
 
	noinv = true;
 
	nofading = true;
 
	disp = '';
 
	theme = 'menu';
 
	onenter = function(s)
 
		in_game = false;
 
	end;
 
	themeInit = function(s)
 
		init_menu(false, {'start', 'load', 'about', 'settings', 'quit'});
 
	end;
 
	themeDispose = function(s)
 
		D {'start'};
 
		D {'load'};
 
		D {'about'};
 
		D {'settings'};
 
		D {'quit'};
 
		timer:stop();
 
	end;
 
	ondecor = function(s, name, press, x, y, btn)
 
		if press then
 
			if name == 'start' then
 
				walk('intro_start');
 
				return true;
 
			elseif  name == 'load' then
 
				instead.menu('load');
 
				return true;
 
			elseif name == 'about' then
 
				walk('intro_info');
 
				return true;
 
			elseif name == 'settings' then
 
				walk('intro_settings');
 
				return true;
 
			elseif  name == 'quit' then
 
				instead.menu('exit');
 
				return true;
 
			end;
 
		end;
 
		return false;
 
	end;
 
	enter = function(s)
 
    if prefs.completed then
 
      snd.music('sound/bgm_credits.ogg');
 
    else
 
      snd.music('sound/bgm_intro.ogg');
 
    end;
 
	end;
 
}
 

	
 
room {
 
	nam = 'intro_start';
 
	notitle = true;
 
	nosave = true;
 
	noautosave = true;
 
	noinv = true;
 
	nofading = true;
 
	disp = '';
 
	theme = 'menu';
 
	themeInit = function(s)
 
		init_menu(true, {'back', 'tutorial', 'start'});
 
	end;
 
	themeDispose = function(s)
 
		D {'back'};
 
		D {'tutorial'};
 
		D {'start'};
 
		D {'textbg'};
 
		timer:stop();
 
	end;
 
	ondecor = function(s, name, press, x, y, btn)
 
		if press then
 
			if name == 'start' then
 
				walk('above_forest');
 
				in_game = true;
 
				return true;
 
			elseif name == 'tutorial' then
 
				walk('tutorial_start');
 
				in_game = true;
 
				return true;
 
			elseif name == 'back' then
 
				walk('main');
 
				return true;
 
			end;
 
		end;
 
		return false;
 
	end;
 
	decor = translate("menu_locale", 'intro');
 
}
 

	
 
room {
 
	nam = 'intro_info';
 
	notitle = true;
 
	nosave = true;
 
	noautosave = true;
 
	noinv = true;
 
	nofading = true;
 
	disp = '';
 
	theme = 'menu';
 
	themeInit = function(s)
 
		init_menu(true, {'back'});
 
	end;
 
	themeDispose = function(s)
 
		D {'start'};
 
		D {'back'};
 
		D {'settings'};
 
		D {'textbg'};
 
		timer:stop();
 
	end;
 
	ondecor = function(s, name, press, x, y, btn)
 
		if press then
 
			if name == 'back' then
 
				if in_game then
 
					walk('menu');
 
				else
 
					walk('main');
 
				end;
 
				return true;
 
			end;
 
		end;
 
		return false;
 
	end;
 
	decor = function(s)
 
		return translate_now("menu_locale", 'credits_author') .. ': Khaelenmore Thaal^' ..
 
			translate_now("menu_locale", 'credits_art') .. ': Sabra Naikomy^' ..
 
			translate_now("menu_locale", 'credits_music') .. ': Khaelenmore Thaal^' ..
 
			translate_now("menu_locale", 'credits_engine') .. ': INSTEAD ' .. translate_now("menu_locale", 'credits_engine_author') .. ' {$link https://github.com/instead-hub/instead/}^' ..
 
			translate_now("menu_locale", 'credits_sounds') .. ': {$link https://freesound.org/} CC-0^' ..
 
			translate_now("menu_locale", 'credits_fonts') .. ': VileR {$link https://int10h.org/oldschool-pc-fonts/} CC-BY-SA 4.0^^' ..
 
      translate_now("menu_locale", 'credits_closed_beta') .. ': ' .. closed_beta_participants .. '^^'..
 
			translate_now("menu_locale", 'credits_thanks');
 
	end;
 
}
 

	
 
obj {
 
	nam = 'empty';
 
	dsc = '^';
 
}
 

	
 
room {
 
	nam = 'intro_settings';
 
	nofollow = true;
 
	notitle = true;
 
	nosave = true;
 
	noautosave = true;
 
	noinv = true;
 
	nofading = true;
 
	disp = '';
 
	theme = 'menu';
 
	themeInit = function(s)
 
    init_menu(true, {'back'});
 
	end;
 
	themeDispose = function(s)
 
		D {'start'};
 
		D {'about'};
 
		D {'back'};
 
		D {'textbg'};
 
		timer:stop();
 
	end;
 
	ondecor = function(s, name, press, x, y, btn)
 
		if press then
 
			if name == 'back' then
 
				if in_game then
 
					stop_menu();
 
					walkback();
 
				else
 
					walk('main');
 
				end;
 
				return true;
 
			end;
 
		end;
 
		return false;
 
	end;
 
}:with {
 
	obj {
 
		nam = '#system_settings';
 
		dsc = function(s)
 
			return fmt.c(fmt.b('{[' .. translate_now("menu_locale", 'system_settings') .. ']}^^'));
 
		end;
 
		act = function(s)
 
			instead.menu('settings');
 
		end;
 
	};
 
	obj {
 
		nam = '#language';
 
		dsc = function(s)
 
			local l = translate_now("menu_locale", 'auto_option');
 
			
 

	
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'language'))));
 
			
 
			local t;
 
			if get_language_saved() == '' then
 
				t = l;
 
			else
 
				t = '{#l_auto|'.. l .. '}';
 
			end;
 
			
 
      for i, language in ipairs(get_language_list()) do
 
        if get_language_saved() == language[1] then
 
          t = t .. ' | ' .. language[2];
 
        else
 
          t = t .. ' | ' .. '{@set_language ' .. language[1] .. '|' .. language[2] .. '}';
 
        end;
 
      end;
 
		
 
			pn(fmt.c(t) .. '^')
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#l_auto';
 
			act = function(s)
 
				set_language('');
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#graphics_scale';
 
		dsc = function(s)
 
			local graphic_scale = get_graphic_scale();
 
			
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'graphic_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'))
 
		
 
			local t = '';
 
			
 
			if graphic_scale > 1 then
 
				t = t .. '{#g_less|<<} ';
 
			else
 
				t = t .. '<< ';
 
			end;
 

	
 
			local l =  translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(graphic_scale * 100) .. '%)';
 
			
 
			
 
			if prefs.graphic_scale then
 
				l = tostring(graphic_scale * 100) .. '%';
 
			end;
 

	
 
			t = t .. l;
 

	
 
			local w, h = diretheme.get_screen();
 
		
 
			local wsmax = math.floor(w / 640);
 
			local smax = math.floor(h / 480);
 
			if wsmax < smax then
 
				smax = wsmax;
 
			end;
 
		
 
			if smax < 1 then
 
				smax = 1;
 
			end;
 
		
 
			if graphic_scale < smax then
 
				t = t .. ' {#g_more|>>}';
 
			else
 
				t = t .. ' >>';
 
			end;
 
		
 
			pn(fmt.c(t) .. '^');
 
		end;
 
		act = function(s)
 
			prefs.graphic_scale = false;
 
			prefs:store();
 
		
 
			set_theme(std.call(here(), 'theme'));
 
			menu_sprites = {};
 
			here():themeDispose();
 
			stop_menu();
 
			here():themeInit();
 
			walk(here());
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#g_less';
 
			act = function(s)
 
				prefs.graphic_scale = get_graphic_scale() - 1;
 
				
 
				prefs:store();
 
		
 
				set_theme(std.call(here(), 'theme'));
 
				menu_sprites = {};
 
				here():themeDispose();
 
				stop_menu();
 
				here():themeInit();
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#g_more';
 
			act = function(s)
 
				prefs.graphic_scale = get_graphic_scale() + 1;
 
				
 
				prefs:store();
 
		
 
				set_theme(std.call(here(), 'theme'));
 
				menu_sprites = {};
 
				here():themeDispose();
 
				stop_menu();
 
				here():themeInit();
 
				walk(here());
 
			end;
 
		};
 
	};
 
  obj {
 
		nam = '#cursor_scale';
 
		dsc = function(s)
 
			local cursor_scale = get_cursor_scale();
 
			
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'cursor_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'))
 
		
 
			local t = '';
 
			
 
			if cursor_scale > 1 then
 
				t = t .. '{#cursor_less|<<} ';
 
			else
 
				t = t .. '<< ';
 
			end;
 

	
 
			local l = translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(cursor_scale * 100) .. '%)';
 
			
 
			if prefs.cursor_scale then
 
				l = tostring(cursor_scale * 100) .. '%';
 
			end;
 

	
 
			t = t .. l;
 

	
 
			local w, h = diretheme.get_screen();
 
		
 
			local wsmax = math.floor(w / 640);
 
			local smax = math.floor(h / 480);
 
			if wsmax < smax then
 
				smax = wsmax;
 
			end;
 
		
 
			if smax < 1 then
 
				smax = 1;
 
			end;
 
		
 
			if cursor_scale < smax then
 
				t = t .. ' {#cursor_more|>>}';
 
			else
 
				t = t .. ' >>';
 
			end;
 
		
 
			pn(fmt.c(t) .. '^');
 
		end;
 
		act = function(s)
 
			prefs.cursor_scale = false;
 
			prefs:store();
 
		
 
			set_theme(std.call(here(), 'theme'));
 
			menu_sprites = {};
 
			here():themeDispose();
 
			stop_menu();
 
			here():themeInit();
 
			walk(here());
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#cursor_less';
 
			act = function(s)
 
				prefs.cursor_scale = get_cursor_scale() - 1;
 
				
 
				prefs:store();
 
		
 
				set_theme(std.call(here(), 'theme'));
 
				menu_sprites = {};
 
				here():themeDispose();
 
				stop_menu();
 
				here():themeInit();
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#cursor_more';
 
			act = function(s)
 
				prefs.cursor_scale = get_cursor_scale() + 1;
 
				
 
				prefs:store();
 
		
 
				set_theme(std.call(here(), 'theme'));
 
				menu_sprites = {};
 
				here():themeDispose();
 
				stop_menu();
 
				here():themeInit();
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#font_scale';
 
		dsc = function(s)		
 
			local graphic_scale = get_graphic_scale();
 
			local font_scale = get_font_scale();
 
				
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'font_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'));
 
		
 
			local t = '';
 
		
 
			if font_scale > 50 then
 
				t = t .. '{#f_less|<<} ';
 
			else
 
				t = t .. '<< ';
 
			end;
 
		
 
			if prefs.font_scale then
 
				t = t .. tostring(font_scale) .. '%';
 
			else
 
				t = t .. translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(font_scale) .. '%)';
 
			end;
 
		
 
		
 
			if font_scale < graphic_scale * 200 then
 
				t = t .. ' {#f_more|>>}';
 
			else
 
				t = t .. ' >>';
 
			end;
 
		
 
			pn(fmt.c(t) .. '^');
 
		end;
 
		act = function(s)
 
			prefs.font_scale = false;
 
		
 
			prefs:store();
 
			set_theme(std.call(here(), 'theme'));
 
			walk(here());
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#f_less';
 
			act = function(s)
 
				prefs.font_scale = get_font_scale() - 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#f_more';
 
			act = function(s)
 
				prefs.font_scale = get_font_scale() + 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#inv_font_scale';
 
		dsc = function(s)		
 
			local graphic_scale = get_graphic_scale();
 
			local font_scale = get_inventory_font_scale();
 
				
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'inventory_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'));
 
		
 
			local t = '';
 
		
 
			if font_scale > 50 then
 
				t = t .. '{#if_less|<<} ';
 
			else
 
				t = t .. '<< ';
 
			end;
 
		
 
			if prefs.inventory_scale then
 
				t = t .. tostring(font_scale) .. '%';
 
			else
 
				t = t .. translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(font_scale) .. '%)';
 
			end;
 
		
 
		
 
			if font_scale < graphic_scale * 200 then
 
				t = t .. ' {#if_more|>>}';
 
			else
 
				t = t .. ' >>';
 
			end;
 
		
 
			pn(fmt.c(t) .. '^');
 
		end;
 
		act = function(s)
 
			prefs.inventory_scale = false;
 
		
 
			prefs:store();
 
			set_theme(std.call(here(), 'theme'));
 
			walk(here());
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#if_less';
 
			act = function(s)
 
				prefs.inventory_scale = get_inventory_font_scale() - 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#if_more';
 
			act = function(s)
 
				prefs.inventory_scale = get_inventory_font_scale() + 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#text_width';
 
		dsc = function(s)
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'text_width'))));
 
			local t = '';
 
			if prefs.text_width > 40 then
 
				t = t .. '{#fw_less|<<} ';
 
			else
 
				t = t .. '<< ';
 
			end;
 

	
 
			t = t .. tostring(prefs.text_width) .. '%';
 
		
 
			if prefs.text_width < 100 then
 
				t = t .. ' {#fw_more|>>}';
 
			else
 
				t = t .. ' >>';
 
			end;
 
		
 
			pn(fmt.c(t) .. '^');
 
		end;
 
		act = function(s)
 
			if not prefs.text_width then
 
				prefs.text_width = 100;
 
			end;
 
		
 
			prefs.text_width = prefs.text_width + 20;
 
		
 
			if prefs.text_width > 100 then
 
				prefs.text_width = 40;
 
			end;
 
		
 
			prefs:store();
 
		
 
			walk(here());
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#fw_less';
 
			act = function(s)
 
				prefs.text_width = prefs.text_width - 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#fw_more';
 
			act = function(s)
 
				prefs.text_width = prefs.text_width + 10;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#text_alignment';
 
		dsc = function(s)
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'text_alignment'))));
 
			
 
			local t = '';
 
		
 
			if prefs.text_alignment == 1 then
 
				t = t .. translate_now("menu_locale", 'text_alignment_justify') .. ' | ';
 
			else
 
				t = t .. '{#align_just|' .. translate_now("menu_locale", 'text_alignment_justify') .. '} | ';
 
			end;
 
			
 
		
 
			if prefs.text_alignment == 2 then
 
				t = t .. translate_now("menu_locale", 'text_alignment_left');
 
			else
 
				t = t .. '{#align_left|' .. translate_now("menu_locale", 'text_alignment_left') .. '}';
 
			end;
 
			pn(fmt.c(t) .. '^');
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#align_just';
 
			act = function(s)
 
				prefs.text_alignment = 1;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#align_left';
 
			act = function(s)
 
				prefs.text_alignment = 2;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#padding';
 
		dsc = function(s)
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'padding'))));
 
			
 
			local t = '';
 
		
 
			if prefs.text_padding == 1 then
 
				t = t .. translate_now("menu_locale", 'normal') .. ' | ';
 
			else
 
				t = t .. '{#pad_norm|' .. translate_now("menu_locale", 'normal') .. '} | ';
 
			end;
 
			
 
		
 
			if prefs.text_padding == 2 then
 
				t = t .. translate_now("menu_locale", 'extended');
 
			else
 
				t = t .. '{#pad_large|' .. translate_now("menu_locale", 'extended') .. '}';
 
			end;
 
			pn(fmt.c(t) .. '^');
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#pad_norm';
 
			act = function(s)
 
				prefs.text_padding = 1;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#pad_large';
 
			act = function(s)
 
				prefs.text_padding = 2;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
	obj {
 
		nam = '#interval';
 
		dsc = function(s)
 
			pn(fmt.c(fmt.b(translate_now("menu_locale", 'interval'))));
 
			
 
			local t = '';
 
		
 
			if prefs.text_interval == 1 then
 
				t = t .. translate_now("menu_locale", 'normal') .. ' | ';
 
			else
 
				t = t .. '{#int_norm|' .. translate_now("menu_locale", 'normal') .. '} | ';
 
			end;
 
			
 
		
 
			if prefs.text_interval == 2 then
 
				t = t .. translate_now("menu_locale", 'extended');
 
			else
 
				t = t .. '{#int_large|' .. translate_now("menu_locale", 'extended') .. '}';
 
			end;
 
			pn(fmt.c(t) .. '^');
 
		end;
 
	}: with {
 
		obj {
 
			nam = '#int_norm';
 
			act = function(s)
 
				prefs.text_interval = 1;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
		obj {
 
			nam = '#int_large';
 
			act = function(s)
 
				prefs.text_interval = 2;
 
			
 
				prefs:store();
 
				set_theme(std.call(here(), 'theme'));
 
				walk(here());
 
			end;
 
		};
 
	};
 
  obj {
 
    nam = '#textinput';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'use_text_input'))));
 
      
 
      local t = '';
 
      if prefs.text_input then
 
        t = '{#ti_disable|' .. translate_now("menu_locale", 'disable') .. '} | ' .. translate_now("menu_locale", 'enable');
 
			else
 
				t = translate_now("menu_locale", 'disable') .. ' | {#ti_enable|' .. translate_now("menu_locale", 'enable') .. '}';
 
			end;
 
      
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#ti_disable';
 
      act = function(s)
 
        prefs.text_input = false;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#ti_enable';
 
      act = function(s)
 
        prefs.text_input = true;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
  };
 
}
 

	
 
room {
 
	nosave = true;
 
	noautosave = true;
 
@@ -1192,633 +294,16 @@ room {
 
	}
 
}
 

	
 
function set_theme(t)  
 
  -- resetting defaults
 
  fmt.para = true;
 
  std.scene_delim = '^^';
 
  theme.set('snd.click', 'sound/click.wav');
 
  
 
	D {'settings_open'};
 

	
 
  local tname, tcolor_text, tcolor_link, tcolor_alink;
 

	
 
  local width, height = diretheme.get_screen();
 
	
 
  -- 640x480 is the base size
 
	local scale = get_graphic_scale();
 
	local max_scale_x = math.floor(width / 640);
 
	local max_scale_y = math.floor(height / 480);
 
	-- Fail-safe for too small screen
 
  if max_scale_x < 1 then max_scale_x = 1; end;
 
  if max_scale_y < 1 then max_scale_y = 1; end;
 
	  
 
  local max_scale = math.min(max_scale_x, max_scale_y);
 
  
 
  if scale > max_scale then
 
    scale = max_scale;
 
    prefs.graphic_scale = false;
 
  end;
 
  
 
  local cursor_scale = get_cursor_scale();
 
  if cursor_scale > 2 * max_scale then
 
    cursor_scale = 2 * max_scale;
 
  end;
 
  
 
	local font_scale = get_font_scale() / 100;
 
	if font_scale > 2 * scale then
 
		font_scale = scale;
 
		prefs.font_scale = font_scale * 100
 
	end;
 
	
 
  local inv_scale = get_inventory_font_scale() / 100;
 
	
 
	local text_align = 'justify';
 
	if prefs.text_alignment == 2 then
 
		text_align = 'left';
 
	end;
 
	
 
	local hpadding, vpadding = 0, 0;
 
	if prefs.text_padding == 2 then
 
		hpadding, vpadding = 24, 12;
 
	end;
 
	
 
	local interval = '1.0';
 
	if prefs.text_interval == 2 then
 
		interval = '1.5';
 
	end;
 
	
 
	local canvas = diretheme.set_canvas({rmax = 2, rmin = 1});
 
	diretheme.set_scale(scale);
 

	
 
  local scaled_width, scaled_height = diretheme.get_scaled_screen();
 

	
 
	current_theme = t;
 
  local tvariant = 0;
 
  if t:match("_bw$") then
 
    t = t:gsub("_bw$", "");
 
    tvariant = 1;
 
  end;
 
  
 
  -- Most text area settings are per-theme
 
  local text_area = {};
 
  
 
  -- Hide inventory by default
 
  local inventory = {
 
    rect = {
 
      lx = width + 1,
 
      y = 0,
 
      wmax = 1,
 
      wmin = 1,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    }
 
  };
 
  
 
  -- And image
 
  local image_area = {
 
    rect = {
 
      lx = width + 1,
 
      ax = 1,
 
      wmax = 584,
 
      hmax = 258,
 
      wmin = 584,
 
      hmin = 258
 
    };
 
    bg = {};
 
    mode = 'float';
 
  };
 
  
 
  -- Preset colors
 
  local theme_directory = nil;
 
  local text_color, link_color, alink_color;
 
  local inv_color, inv_link, inv_alink;
 
  if t == 'terminal' or t == 'black' then
 
    diretheme.set_canvas({
 
      lx = 0, ty = 0, rx = 0, by = 0
 
    });
 
    text_color = '#D0FFFF';
 
    link_color = '#ABDECE';
 
    alink_color = '#9BCEBE';  
 
    theme_directory = 'images/tower_theme';
 
    
 
    text_area.text = {
 
      font = 'fonts/vga.ttf',
 
      color = text_color,
 
      link = link_color,
 
      alink = alink_color,
 
      size = 16
 
    };
 
  else
 
    canvas = diretheme.set_canvas({
 
      lx = 0, ty = 0, rx = 0, by = 0, rmin = 1.2, rmax = 1.8
 
    });
 
    
 
    if t == 'menu' or t == 'main' or t == 'ending_outside' or t == 'credits_outside' then
 
      inv_color = '#403426';
 
      inv_link = '#403426';
 
      inv_alink = '#61503A';
 
      
 
      if tvariant == 0 then
 
        text_color = '#000504';
 
        link_color = '#80382A';
 
        alink_color = '#80605D';
 
        theme_directory = 'images/main_theme';
 
      else
 
        text_color = '#040404';
 
        link_color = '#434343';
 
        alink_color = '#585858';
 
        theme_directory = 'images/bwm_theme';
 
      end;
 
      
 
      text_area.uppos = {ax=1, x = 12, y = 0, ay=-1};
 
      text_area.downpos = {ax=1, x = 12, y = 0, ay=1};
 
      text_area.bg = {
 
        image = theme_directory .. '/text_bg.png',
 
        options = {
 
          mode = 6,
 
          drawmethod = 2,
 
          slice = {
 
            x = 9,
 
            y = 6,
 
            w = 96,
 
            h = 96
 
          },
 
          scale = true
 
        }
 
      };
 
    elseif t == 'tower' or t == 'ending_inside' or t == 'credits_inside' then
 
      inv_color = '#6b9e8e';
 
      inv_link = '#9bcebe';
 
      inv_alink = '#Bbeede';
 
      
 
      if tvariant == 0 then
 
        text_color = '#93DDCD';
 
        link_color = '#E0F0EA';
 
        alink_color = '#FFFFFF';
 
        theme_directory = 'images/tower_theme';
 
      else
 
        text_color = '#ADADAD';
 
        link_color = '#CDCDCD';
 
        alink_color = '#BDBDBD';
 
        theme_directory = 'images/bwt_theme';
 
      end;
 
      
 
      text_area.uppos = {ax=1, x = 16, y = 0, ay=-1};
 
      text_area.downpos = {ax=1, x = 16, y = 0, ay=1};
 
      text_area.bg = {
 
        image = theme_directory .. '/text_bg.png',
 
        options = {
 
          mode = 6,
 
          drawmethod = 2,
 
          slice = {
 
            x = 6,
 
            y = 6,
 
            w = 128,
 
            h = 128
 
          },
 
          scale = true
 
        }
 
      };
 
    end;
 
    
 
    if t == 'ending_inside' or t == 'ending_outside' then
 
      canvas = diretheme.set_canvas({
 
        lx = 0, ty = 0, rx = 0, by = 0
 
      });
 
    end;
 
  
 
    text_area.align = text_align;
 
    text_area.height = interval;
 
    text_area.scaleui = true;
 
    text_area.up = theme_directory .. '/arrow_up.png';
 
    text_area.down = theme_directory .. '/arrow_down.png';
 
    text_area.text = {
 
      font = 'fonts/{sans,sans-b,sans-i,sans-bi}.ttf',
 
      color = text_color,
 
      link = link_color,
 
      alink = alink_color,
 
      size = math.floor(16 * font_scale),
 
      noscale = true
 
    };
 
    
 
    inventory.up = theme_directory .. '/arrow_up.png';
 
    inventory.down = theme_directory .. '/arrow_down.png';
 
    inventory.uppos = {ax=1, x = 8, y = 0, ay=-1};
 
    inventory.downpos = {ax=1, x = 8, y = 0, ay=1};
 
    inventory.text = {
 
      color = inv_color,
 
      link = inv_link,
 
      alink = inv_alink,
 
      size = math.floor(16 * inv_scale),
 
      noscale = true
 
    };
 
    inventory.height = interval;
 
  end;
 
  
 
  if t == 'main' or t == 'tower' then
 
    std.scene_delim = '^' .. fmt.c(fmt.img(theme_directory .. '/line_separator.png')) .. '^'
 
  end;
 
  
 
 -- And menu, but need theme_directory to have proper button image
 
  local menu = {
 
    button = {
 
      image = theme_directory ..'/menu.png';
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
    buttonrect = {
 
      x = width + 1,
 
      y = 0,
 
      w = 50,
 
      h = 16
 
    };
 
    text = {
 
      size = math.floor(15 * font_scale),
 
      noscale = true
 
    },
 
    height = interval,
 
    scaleui = true;
 
  };
 
  
 
  local cursor, active_cursor = 'images/cursor.png', 'images/cursor_active.png';
 
  if tvariant == 1 then
 
    cursor, active_cursor = 'images/cursor_bw.png', 'images/cursor_active_bw.png'
 
  end;
 
  
 
  diretheme.set_cursor({
 
		x = 2,
 
		y = 2,
 
		normal = cursor,
 
		active = active_cursor,
 
    scale = true;
 
		scalefactor = cursor_scale;
 
	});
 
  
 
	if t == 'menu' then
 
		diretheme.set_background('#89C6AE', 255, 'images/main_theme/background.png', {mode=2, scale=true});
 
    local padding_delta = -10;
 
    if height < 520 * scale then
 
      padding_delta = -95;
 
    end;
 
    
 
    text_area.rect = {
 
      ty = padding_delta + vpadding,
 
      wmax = 538 - hpadding * 2,
 
      hmax = 322 - vpadding * 2,
 
      ay = 0
 
    };
 
    text_area.text = text_area.text;
 
    text_area.uppos = {ax=1, x = 15, y = 65, ay=-1};
 
    text_area.downpos = {ax=1, x = 15, y = -20, ay=1};
 
    text_area.contentrect = {wf=prefs.text_width / 100};
 
    text_area.bg = '';
 
	elseif t == 'main' then
 
		diretheme.set_background('#89C6AE', 255, 'images/main_theme/background.png', {mode=2, scale = true});
 
		
 
		local image_bg = 'images/main_theme/picture_bg.png';
 
		if canvas.w < 800 * scale then
 
			image_bg = 'images/main_theme/picture_bg_small.png';
 
		end;
 
		
 
    image_area.leftof = 'inv';
 
    image_area.topof = 'text';
 
    image_area.rect = {
 
      ax = 0,
 
      wmax = 584,
 
      hmax = 265,
 
      wmin = 584,
 
      hmin = 265
 
    };
 
    image_area.contentrect = {
 
      ax = 0,
 
      ay = -1,
 
      ty = 11,
 
      wmin = 320,
 
      hmin = 240,
 
      wmax = 320,
 
      hmax = 240
 
    };
 
    image_area.bg = {
 
      image = image_bg,
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
		
 
    text_area.leftof = 'inv';
 
    text_area.rect = {
 
      lx = 7,
 
      ty = 266,
 
      rx = 8,
 
      by = 2,
 
      ax = 1,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 21 + hpadding,
 
      rx = 21 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf=prefs.text_width / 100
 
    };
 
    
 
		inventory.rect = {
 
      rx = 2,
 
      y = 2,
 
      wmax = 208,
 
      wmin = 208,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    };
 
    inventory.contentrect = {
 
      lx = 25,
 
      rx = 25,
 
      ty = 40,
 
      by = 40
 
    };
 
    inventory.bg = {
 
      image = function(tpx, rect)
 
        local top = pixels.new('images/main_theme/inv_top.png'):scale(scale, scale, false);
 
        local mid = pixels.new('images/main_theme/inv_mid.png'):scale(scale, scale, false);
 
        local bottom = pixels.new('images/main_theme/inv_bottom.png'):scale(scale, scale, false);
 
        
 
        dire_draw_tile(mid, tpx, { x=rect.x + 11 * scale, y=rect.y + 10 * scale, w=rect.w - 22 * scale, h = rect.h - 20 * scale}, {});
 
        dire_draw_center(top, tpx, {x=rect.x, y=rect.y, w=208 * scale, h=60 * scale});
 
        dire_draw_center(bottom, tpx, {x=rect.x, y=rect.y + rect.h - 60 * scale, w=208 * scale, h=60 * scale});
 
      end,
 
      options = {scale = true}
 
    };
 

	
 
    menu.buttonrect = {
 
      wmin = 50,
 
      wmax = 50,
 
      hmin = 16,
 
      hmax = 16,
 
      by = 12,
 
      ay = 1,
 
      rx = 81,
 
      ax = 1
 
    };
 

	
 
		D {
 
			'settings_open', 
 
			'raw',
 
			x=canvas.x + canvas.w - 33 * scale, 
 
			y=canvas.y + canvas.h - 30 * scale, 
 
      xc=0,
 
      yc=0,
 
      w=24 * scale,
 
      h=22 * scale,
 
			click=true
 
		};
 
	elseif t == 'tower' then
 
		diretheme.set_background('#181E37', 255, 'images/tower_theme/background.png', {mode=2, scale=true});
 
		
 
		local image_bg = 'images/tower_theme/picture_bg.png';
 
		if canvas.w < 800 * scale then
 
			image_bg = 'images/tower_theme/picture_bg_small.png';
 
		end;
 
		
 
    image_area.leftof='inv';
 
    image_area.topof='text';
 
    image_area.rect = {
 
      ax = 0,
 
      maxw = 584,
 
      maxh = 258,
 
      minw = 584,
 
      minh = 258
 
    };
 
    image_area.contentrect = {
 
      ax = 0,
 
      ay = -1,
 
      ty = 12,
 
      wmin = 320,
 
      hmin = 240,
 
      wmax = 320,
 
      hmax = 240
 
    };
 
    image_area.bg = {
 
      image = image_bg,
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
		
 
    text_area.leftof = 'inv';
 
    text_area.rect = {
 
      lx = 7,
 
      ty = 266,
 
      rx = 8,
 
      by = 2,
 
      ax = 1,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 21 + hpadding,
 
      rx = 21 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
  
 
    inventory.rect = {
 
      rx = 2,
 
      y = 2,
 
      wmax = 208,
 
      wmin = 208,
 
      ty = 2,
 
      by = 2,
 
      ax = 1
 
    };
 
    inventory.contentrect = {
 
      lx = 12,
 
      rx = 16,
 
      ty = 10,
 
      by = 40
 
    };
 
    inventory.bg = {
 
      image = function(tpx, rect)
 
        local bg = pixels.new('images/tower_theme/inv_bg.png'):scale(scale, scale, false);
 
        local icon = pixels.new('images/tower_theme/settings.png'):scale(scale, scale, false);
 
        
 
        local tbg, tbg_slice = dire_slice_9(bg, {
 
          x = 7 * scale,
 
          y = 11 * scale,
 
          w = 194 * scale,
 
          h = 38 * scale
 
        })
 
        
 
        dire_draw_9(tbg, tpx, rect, {parts=tbg_slice});
 
        
 
        dire_draw_center(icon, tpx, {x = rect.x + rect.w - 33 * scale, y = rect.y + rect.h - 35 * scale, w = 24 * scale, h = 22 * scale});
 
      end
 
    };
 
    
 
    menu.buttonrect = {
 
      wmin = 50,
 
      wmax = 50,
 
      hmin = 16,
 
      hmax = 16,
 
      by = 20,
 
      ay = 1,
 
      rx = 81,
 
      ax = 1
 
    };
 
		
 
		D {
 
			'settings_open', 
 
			'raw', 
 
			x=canvas.x + canvas.w - 35 * scale, 
 
			y=canvas.y + canvas.h - 37 * scale,
 
      xc=0,
 
      yc=0,
 
      w=24 * scale,
 
      h=22 * scale,
 
			click=true
 
		};
 
	elseif t == 'terminal' then
 
		theme.set('snd.click', '');
 
		
 
    local upshift = 0;
 
    if height / scale < 540 then
 
      upshift = 19;
 
    end;
 
    
 
    local px = pixels.new(scaled_width, scaled_height);
 
		local tiles_src = pixels.new('images/terminal/table_tiles.png');
 
    local backdrop_src = pixels.new('images/terminal/backdrop.png');
 
		
 
		local tabletop = scaled_height - 422 - upshift
 
		if tabletop < 335 - upshift then
 
			tabletop = 335 - upshift;
 
		end;
 
		
 
		dire_draw_tile(tiles_src, px, {
 
			w = scaled_width,
 
			h = 422,
 
			x = 0,
 
			y = tabletop
 
		}, {xoffset = -1, yoffxet = -1});
 

	
 
    dire_draw_tile(backdrop_src, px, {
 
			w = scaled_width,
 
			h = tabletop,
 
			x = 0,
 
			y = 0
 
		}, {xoffset = -1, yoffset = -2});
 
		
 
		diretheme.set_background('#181E37', 255, px, {mode=5});
 
		
 
		local ty
 
		if here().ticks ~= nil and here().ticks < 128 and here().graphical then
 
			ty = 72
 
		else
 
			ty = 2
 
		end;		
 

	
 
    text_area.rect = {
 
      ty = 0 - upshift,
 
      by = 189,
 
      wmin = 808,
 
      hmin = 568,
 
      wmax = 808,
 
      hmax = 568,
 
      ax = 0,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      wmax = 569,
 
      hmax = 390,
 
      ty = ty,
 
      ax = 0,
 
      ay = -2,
 
      dx = -16
 
    };
 
    text_area.bg = {
 
      image = 'images/terminal/bg_text.png',
 
      options = {
 
        mode = 1,
 
        scale = true
 
      }
 
    };
 
	elseif t == 'ending_outside' then
 
		diretheme.set_background('#89C6AE', 255, theme_directory .. '/background.png', {mode=2, scale=true});
 
		
 
    text_area.rect = {
 
      lx = 24,
 
      rx = 24,
 
      wmax = 600,
 
      hmax = 160,
 
      by = 24,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 18 + hpadding,
 
      rx = 18 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
	elseif t == 'ending_inside' then
 
		diretheme.set_background('#181E37', 255, theme_directory .. '/background.png', {mode=2});
 
		
 
    text_area.rect = {
 
      lx = 24,
 
      rx = 24,
 
      wmax = 600,
 
      hmax = 160,
 
      by = 24,
 
      ay = 1
 
    };
 
    text_area.contentrect = {
 
      lx = 18 + hpadding,
 
      rx = 18 + hpadding,
 
      ty = 14 + vpadding,
 
      by = 14 + vpadding,
 
      wf = prefs.text_width / 100
 
    };
 
  elseif t == 'credits_outside' or t == 'credits_inside' then
 
    -- text scaled on top of picture
 
    text_area.rect = {
 
      wmax = 960,
 
      hmax = 600
 
    };
 
    text_area.bg = {};
 
    text_area.align = 'center';
 
    text_area.content_rect = {
 
      wf = prefs.text_width / 100
 
    };
 
	elseif t == 'black' then    
 
		fmt.para = false;
 
		diretheme.set_background('#000000', 255);
 
		
 
		text_area.rect = {
 
      wmax = 600,
 
      hmax = 400,
 
      ax = 0,
 
      ay = 0
 
    };
 
    text_area.align = 'center';
 
	end;
 
  
 
  diretheme.set_image_area(image_area, true);
 
  diretheme.set_text_area(text_area, true);
 
  diretheme.set_inventory_area(inventory, true);
 
  
 
  diretheme.set_menu(menu, true);
 
end;
 

	
 
function start(load)
 
	dprint('start', load);
 
  last_location = here().nam;
 
	local w, h = diretheme.get_screen();
 
	local name = theme.name();
 
  if instead.tiny then
 
    if not load then
 
      take('tiny_ingame_settings');
 
    end;
 
  else
 
	if here() ~= _'theme_warning' and here() ~= _'resolution_warning' then
 
		if name ~= '.' then
 
			walkin('theme_warning');
 
@@ -1836,6 +321,7 @@ function start(load)
 
      game:lastdisp(r);
 
		end;
 
	end;
 
  end;
 
  
 
	set_theme(std.call(here(), 'theme'));
 
	
 
@@ -1991,4 +477,15 @@ function init()
 
	if not prefs.text_width then
 
		prefs.text_width = 100;
 
	end;
 
  
 
  if not instead.tiny and prefs.simplified_animation == nil then
 
    -- Tiny performance test
 
    local a = sprite.new(640, 480);
 
    local b = sprite.new(640, 480);
 
    local t = instead.ticks();
 
    b:copy(a, 0, 0);
 
    t = instead.ticks() - t;
 
    dprint(t);
 
    prefs.simplified_animation = t > 10;
 
end;
 
end;
menu.lua
Show inline comments
 
new file 100644
 
function get_sprite(path, transparent)
 
  local s = diretheme.get_scale();
 
  
 
  if not menu_sprites[path] then
 
    if s ~= 1 then
 
      menu_sprites[path] = pixels.new(path):scale(s, s):sprite();
 
    else
 
      menu_sprites[path] = sprite.new(path);
 
    end;
 
    if transparent then
 
      menu_sprites[path]:colorkey(transparent);
 
    end;
 
  end;
 
  
 
  return menu_sprites[path];
 
end;
 

	
 
function get_button(button_name)
 
  local path = 'images/title/' .. button_name .. '.' .. get_language() .. '.png';
 
  if menu_sprites[path] then
 
    return menu_sprites[path];
 
  end;
 
  
 
  local sprite = nil;
 
  
 
  --Check if file exists(only used here, so not moved to function)
 
  local f = io.open(path, 'r');  
 
  if f then
 
    f:close();
 
    sprite = get_sprite(path, 'fuchsia')
 
  end;
 
  
 
  if not sprite then
 
    local path = 'images/title/' .. button_name .. '.' .. get_default_language() .. '.png';
 
    sprite = get_sprite(path, 'fuchsia');
 
  end;
 
  return sprite
 
end;
 

	
 
function stop_menu()
 
  D {'bgframe'};
 
  D {'background'};
 
  D {'bganim'};
 
  D {'title'};
 
end;
 

	
 
function init_menu(with_text, buttons)
 
  local scale = diretheme.get_scale();
 
  
 
  local w = 960 * scale;
 
  local h = 600 * scale;
 
  local sw, sh = diretheme.get_screen();
 
  local ox = math.floor((sw - w) / 2);
 
  local oy = math.floor((sh - h) / 2)
 
  
 
  local button_dy = 0;
 
  local button_dx = 0;
 
  if sh < 520 * scale then
 
    button_dy = -50;
 
    button_dx = 20;
 
  end;
 
  
 
  local btop = 515;
 
  
 
  if #buttons == 5 then
 
    btop = 435;
 
  end;
 
  
 
  if sh <= 560 * scale then
 
    btop = btop - 20;
 
  end;
 
  
 
  if #buttons == 1 then
 
    D {
 
      buttons[1], 
 
      'img', 
 
      get_button(buttons[1]),
 
      x=ox + 398 * scale, 
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
  else
 
    D {
 
      buttons[1], 
 
      'img', 
 
      get_button(buttons[1]),
 
      x=ox + (157 + button_dx) * scale, 
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
    D {
 
      buttons[2], 
 
      'img', 
 
      get_button(buttons[2]),
 
      x=ox + 398 * scale,
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
    D {
 
      buttons[3], 
 
      'img', 
 
      get_button(buttons[3]),
 
      x=ox + (639 - button_dx) * scale,
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
  end;
 
  
 
  if #buttons == 5 then
 
    btop = btop + 80
 
    D {
 
      buttons[4], 
 
      'img', 
 
      get_button(buttons[4]),
 
      x=ox + (278 + button_dx / 2) * scale, 
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
    
 
    D {
 
      buttons[5], 
 
      'img', 
 
      get_button(buttons[5]),
 
      x=ox + (519 - button_dx / 2) * scale, 
 
      y=oy + (btop + button_dy) * scale,
 
      click=true,
 
      background=true,
 
      z=3
 
    };
 
  end;
 
  
 
  if sw > w or sh > h then
 
    local bgframe_pixels = pixels.new('images/main_theme/text_bg.png');
 
    local tbg, tbg_slice;
 
    tbg, tbg_slice = dire_slice_9(bgframe_pixels, {
 
      x = 9,
 
      y = 6,
 
      w = 96,
 
      h = 96
 
    });
 
    
 
    local bgframe = pixels.new(978, 612);
 
    dire_draw_9(tbg, bgframe, {x = 0; y = 0; w = 978; h = 612}, {parts=tbg_slice});
 
    D {'bgframe', 'img', bgframe:scale(scale, scale):sprite(), x=ox - 9 * scale, y=oy - 6 * scale, w=w + 18 * scale, h=h + 12 * scale, background=true, z=5};
 
  end;
 
  
 
  if not D('background') then
 
    D {'background', 'img', get_sprite('images/title/bg.png'), x=ox, y=oy, z=4, background=true};
 
  end;
 

	
 
  if prefs.simplified_animation then
 
    D {'bganim'};
 
  elseif not D('bganim') then
 
    D {'bganim', 'img', get_sprite('images/title/bg_frames.png'), x=ox, y=oy, w=w, h=h, frames=16, z=3, delay=200, animated=true, background=true, frame_data=scale_frame_data({
 
       {},
 
       {x=0, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=0, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=302, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=2880, y=604, w=960, h=302, dx=0, dy=0},
 
       {x=0, y=906, w=960, h=302, dx=0, dy=0},
 
       {x=960, y=906, w=960, h=302, dx=0, dy=0},
 
       {x=1920, y=906, w=960, h=302, dx=0, dy=0}
 
    })};
 
  end;
 
  if not D('title') then
 
    D {'title', 'img', get_sprite('images/title/title.png', 'fuchsia'), x=ox + 230 * scale, y=oy + 88  * scale, z=2, background=true};
 
  end;
 
  
 
  if with_text then
 
    D {'textbg', 'img', get_sprite('images/title/text_background.png', 'fuchsia'), x=ox + 180 * scale, z=1, y=oy + (126 + button_dy) * scale, background=true};
 
    if sh < 520 * scale then
 
      D {'title'}
 
    else
 
      D('title').y = oy + 40 * scale;
 
    end;
 
  else
 
    D('title').y = oy + 88 * scale;
 
  end;
 
  
 
  timer:set(25);
 
end;
 

	
 
room {
 
  nam = 'main';
 
  notitle = true;
 
  nosave = true;
 
  noautosave = true;
 
  noinv = true;
 
  nofading = true;
 
  disp = '';
 
  theme = 'menu';
 
  onenter = function(s)
 
    in_game = false;
 
  end;
 
  themeInit = function(s)
 
    init_menu(false, {'start', 'load', 'about', 'settings', 'quit'});
 
  end;
 
  themeDispose = function(s)
 
    D {'start'};
 
    D {'load'};
 
    D {'about'};
 
    D {'settings'};
 
    D {'quit'};
 
    timer:stop();
 
  end;
 
  ondecor = function(s, name, press, x, y, btn)
 
    if press then
 
      if name == 'start' then
 
        walk('intro_start');
 
        return true;
 
      elseif  name == 'load' then
 
        instead.menu('load');
 
        return true;
 
      elseif name == 'about' then
 
        walk('intro_info');
 
        return true;
 
      elseif name == 'settings' then
 
        walk('intro_settings');
 
        return true;
 
      elseif  name == 'quit' then
 
        instead.menu('exit');
 
        return true;
 
      end;
 
    end;
 
    return false;
 
  end;
 
  enter = function(s)
 
    if prefs.completed then
 
      snd.music('sound/bgm_credits.ogg');
 
    else
 
      snd.music('sound/bgm_intro.ogg');
 
    end;
 
  end;
 
}
 

	
 
room {
 
  nam = 'intro_start';
 
  notitle = true;
 
  nosave = true;
 
  noautosave = true;
 
  noinv = true;
 
  nofading = true;
 
  disp = '';
 
  theme = 'menu';
 
  themeInit = function(s)
 
    init_menu(true, {'back', 'tutorial', 'start'});
 
  end;
 
  themeDispose = function(s)
 
    D {'back'};
 
    D {'tutorial'};
 
    D {'start'};
 
    D {'textbg'};
 
    timer:stop();
 
  end;
 
  ondecor = function(s, name, press, x, y, btn)
 
    if press then
 
      if name == 'start' then
 
        walk('above_forest');
 
        in_game = true;
 
        return true;
 
      elseif name == 'tutorial' then
 
        walk('tutorial_start');
 
        in_game = true;
 
        return true;
 
      elseif name == 'back' then
 
        walk('main');
 
        return true;
 
      end;
 
    end;
 
    return false;
 
  end;
 
  decor = translate("menu_locale", 'intro');
 
}
 

	
 
room {
 
  nam = 'intro_info';
 
  notitle = true;
 
  nosave = true;
 
  noautosave = true;
 
  noinv = true;
 
  nofading = true;
 
  disp = '';
 
  theme = 'menu';
 
  themeInit = function(s)
 
    init_menu(true, {'back'});
 
  end;
 
  themeDispose = function(s)
 
    D {'start'};
 
    D {'back'};
 
    D {'settings'};
 
    D {'textbg'};
 
    timer:stop();
 
  end;
 
  ondecor = function(s, name, press, x, y, btn)
 
    if press then
 
      if name == 'back' then
 
        if in_game then
 
          walk('menu');
 
        else
 
          walk('main');
 
        end;
 
        return true;
 
      end;
 
    end;
 
    return false;
 
  end;
 
  decor = function(s)
 
    return credits_text(false);
 
  end;
 
}
 

	
 
room {
 
  nam = 'intro_settings';
 
  nofollow = true;
 
  notitle = true;
 
  nosave = true;
 
  noautosave = true;
 
  noinv = true;
 
  nofading = true;
 
  disp = '';
 
  theme = 'menu';
 
  themeInit = function(s)
 
    init_menu(true, {'back'});
 
  end;
 
  themeDispose = function(s)
 
    D {'start'};
 
    D {'about'};
 
    D {'back'};
 
    D {'textbg'};
 
    timer:stop();
 
  end;
 
  ondecor = function(s, name, press, x, y, btn)
 
    if press then
 
      if name == 'back' then
 
        if in_game then
 
          stop_menu();
 
          walkback();
 
        else
 
          walk('main');
 
        end;
 
        return true;
 
      end;
 
    end;
 
    return false;
 
  end;
 
}:with {
 
  obj {
 
    nam = '#system_settings';
 
    dsc = function(s)
 
      return fmt.c(fmt.b('{[' .. translate_now("menu_locale", 'system_settings') .. ']}^^'));
 
    end;
 
    act = function(s)
 
      instead.menu('settings');
 
    end;
 
  };
 
  obj {
 
    nam = '#language';
 
    dsc = function(s)
 
      local l = translate_now("menu_locale", 'auto_option');
 
      
 

	
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'language'))));
 
      
 
      local t;
 
      if get_language_saved() == '' then
 
        t = l;
 
      else
 
        t = '{#l_auto|'.. l .. '}';
 
      end;
 
      
 
      for i, language in ipairs(get_language_list()) do
 
        if get_language_saved() == language[1] then
 
          t = t .. ' | ' .. language[2];
 
        else
 
          t = t .. ' | ' .. '{@set_language ' .. language[1] .. '|' .. language[2] .. '}';
 
        end;
 
      end;
 
    
 
      pn(fmt.c(t) .. '^')
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#l_auto';
 
      act = function(s)
 
        set_language('');
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#graphics_scale';
 
    dsc = function(s)
 
      local graphic_scale = get_graphic_scale();
 
      
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'graphic_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'))
 
    
 
      local t = '';
 
      
 
      if graphic_scale > 1 then
 
        t = t .. '{#g_less|<<} ';
 
      else
 
        t = t .. '<< ';
 
      end;
 

	
 
      local l =  translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(graphic_scale * 100) .. '%)';
 
      
 
      
 
      if prefs.graphic_scale then
 
        l = tostring(graphic_scale * 100) .. '%';
 
      end;
 

	
 
      t = t .. l;
 

	
 
      local w, h = diretheme.get_screen();
 
    
 
      local wsmax = math.floor(w / 640);
 
      local smax = math.floor(h / 480);
 
      if wsmax < smax then
 
        smax = wsmax;
 
      end;
 
    
 
      if smax < 1 then
 
        smax = 1;
 
      end;
 
    
 
      if graphic_scale < smax then
 
        t = t .. ' {#g_more|>>}';
 
      else
 
        t = t .. ' >>';
 
      end;
 
    
 
      pn(fmt.c(t) .. '^');
 
    end;
 
    act = function(s)
 
      prefs.graphic_scale = false;
 
      prefs:store();
 
    
 
      set_theme(std.call(here(), 'theme'));
 
      menu_sprites = {};
 
      here():themeDispose();
 
      stop_menu();
 
      here():themeInit();
 
      walk(here());
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#g_less';
 
      act = function(s)
 
        prefs.graphic_scale = get_graphic_scale() - 1;
 
        
 
        prefs:store();
 
    
 
        set_theme(std.call(here(), 'theme'));
 
        menu_sprites = {};
 
        here():themeDispose();
 
        stop_menu();
 
        here():themeInit();
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#g_more';
 
      act = function(s)
 
        prefs.graphic_scale = get_graphic_scale() + 1;
 
        
 
        prefs:store();
 
    
 
        set_theme(std.call(here(), 'theme'));
 
        menu_sprites = {};
 
        here():themeDispose();
 
        stop_menu();
 
        here():themeInit();
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#cursor_scale';
 
    dsc = function(s)
 
      local cursor_scale = get_cursor_scale();
 
      
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'cursor_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'))
 
    
 
      local t = '';
 
      
 
      if cursor_scale > 1 then
 
        t = t .. '{#cursor_less|<<} ';
 
      else
 
        t = t .. '<< ';
 
      end;
 

	
 
      local l = translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(cursor_scale * 100) .. '%)';
 
      
 
      if prefs.cursor_scale then
 
        l = tostring(cursor_scale * 100) .. '%';
 
      end;
 

	
 
      t = t .. l;
 

	
 
      local w, h = diretheme.get_screen();
 
    
 
      local wsmax = math.floor(w / 640);
 
      local smax = math.floor(h / 480);
 
      if wsmax < smax then
 
        smax = wsmax;
 
      end;
 
    
 
      if smax < 1 then
 
        smax = 1;
 
      end;
 
    
 
      if cursor_scale < smax then
 
        t = t .. ' {#cursor_more|>>}';
 
      else
 
        t = t .. ' >>';
 
      end;
 
    
 
      pn(fmt.c(t) .. '^');
 
    end;
 
    act = function(s)
 
      prefs.cursor_scale = false;
 
      prefs:store();
 
    
 
      set_theme(std.call(here(), 'theme'));
 
      menu_sprites = {};
 
      here():themeDispose();
 
      stop_menu();
 
      here():themeInit();
 
      walk(here());
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#cursor_less';
 
      act = function(s)
 
        prefs.cursor_scale = get_cursor_scale() - 1;
 
        
 
        prefs:store();
 
    
 
        set_theme(std.call(here(), 'theme'));
 
        menu_sprites = {};
 
        here():themeDispose();
 
        stop_menu();
 
        here():themeInit();
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#cursor_more';
 
      act = function(s)
 
        prefs.cursor_scale = get_cursor_scale() + 1;
 
        
 
        prefs:store();
 
    
 
        set_theme(std.call(here(), 'theme'));
 
        menu_sprites = {};
 
        here():themeDispose();
 
        stop_menu();
 
        here():themeInit();
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#font_scale';
 
    dsc = function(s)    
 
      local graphic_scale = get_graphic_scale();
 
      local font_scale = get_font_scale();
 
        
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'font_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'));
 
    
 
      local t = '';
 
    
 
      if font_scale > 50 then
 
        t = t .. '{#f_less|<<} ';
 
      else
 
        t = t .. '<< ';
 
      end;
 
    
 
      if prefs.font_scale then
 
        t = t .. tostring(font_scale) .. '%';
 
      else
 
        t = t .. translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(font_scale) .. '%)';
 
      end;
 
    
 
    
 
      if font_scale < graphic_scale * 200 then
 
        t = t .. ' {#f_more|>>}';
 
      else
 
        t = t .. ' >>';
 
      end;
 
    
 
      pn(fmt.c(t) .. '^');
 
    end;
 
    act = function(s)
 
      prefs.font_scale = false;
 
    
 
      prefs:store();
 
      set_theme(std.call(here(), 'theme'));
 
      walk(here());
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#f_less';
 
      act = function(s)
 
        prefs.font_scale = get_font_scale() - 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#f_more';
 
      act = function(s)
 
        prefs.font_scale = get_font_scale() + 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#inv_font_scale';
 
    dsc = function(s)    
 
      local graphic_scale = get_graphic_scale();
 
      local font_scale = get_inventory_font_scale();
 
        
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'inventory_scale')) .. ' {[' .. translate_now("menu_locale", 'auto_option') .. ']}'));
 
    
 
      local t = '';
 
    
 
      if font_scale > 50 then
 
        t = t .. '{#if_less|<<} ';
 
      else
 
        t = t .. '<< ';
 
      end;
 
    
 
      if prefs.inventory_scale then
 
        t = t .. tostring(font_scale) .. '%';
 
      else
 
        t = t .. translate_now("menu_locale", 'auto_option') .. ' (' .. tostring(font_scale) .. '%)';
 
      end;
 
    
 
    
 
      if font_scale < graphic_scale * 200 then
 
        t = t .. ' {#if_more|>>}';
 
      else
 
        t = t .. ' >>';
 
      end;
 
    
 
      pn(fmt.c(t) .. '^');
 
    end;
 
    act = function(s)
 
      prefs.inventory_scale = false;
 
    
 
      prefs:store();
 
      set_theme(std.call(here(), 'theme'));
 
      walk(here());
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#if_less';
 
      act = function(s)
 
        prefs.inventory_scale = get_inventory_font_scale() - 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#if_more';
 
      act = function(s)
 
        prefs.inventory_scale = get_inventory_font_scale() + 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#text_width';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'text_width'))));
 
      local t = '';
 
      if prefs.text_width > 40 then
 
        t = t .. '{#fw_less|<<} ';
 
      else
 
        t = t .. '<< ';
 
      end;
 

	
 
      t = t .. tostring(prefs.text_width) .. '%';
 
    
 
      if prefs.text_width < 100 then
 
        t = t .. ' {#fw_more|>>}';
 
      else
 
        t = t .. ' >>';
 
      end;
 
    
 
      pn(fmt.c(t) .. '^');
 
    end;
 
    act = function(s)
 
      if not prefs.text_width then
 
        prefs.text_width = 100;
 
      end;
 
    
 
      prefs.text_width = prefs.text_width + 20;
 
    
 
      if prefs.text_width > 100 then
 
        prefs.text_width = 40;
 
      end;
 
    
 
      prefs:store();
 
    
 
      walk(here());
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#fw_less';
 
      act = function(s)
 
        prefs.text_width = prefs.text_width - 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#fw_more';
 
      act = function(s)
 
        prefs.text_width = prefs.text_width + 10;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#text_alignment';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'text_alignment'))));
 
      
 
      local t = '';
 
    
 
      if prefs.text_alignment == 1 then
 
        t = t .. translate_now("menu_locale", 'text_alignment_justify') .. ' | ';
 
      else
 
        t = t .. '{#align_just|' .. translate_now("menu_locale", 'text_alignment_justify') .. '} | ';
 
      end;
 
      
 
    
 
      if prefs.text_alignment == 2 then
 
        t = t .. translate_now("menu_locale", 'text_alignment_left');
 
      else
 
        t = t .. '{#align_left|' .. translate_now("menu_locale", 'text_alignment_left') .. '}';
 
      end;
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#align_just';
 
      act = function(s)
 
        prefs.text_alignment = 1;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#align_left';
 
      act = function(s)
 
        prefs.text_alignment = 2;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#padding';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'padding'))));
 
      
 
      local t = '';
 
    
 
      if prefs.text_padding == 1 then
 
        t = t .. translate_now("menu_locale", 'normal') .. ' | ';
 
      else
 
        t = t .. '{#pad_norm|' .. translate_now("menu_locale", 'normal') .. '} | ';
 
      end;
 
      
 
    
 
      if prefs.text_padding == 2 then
 
        t = t .. translate_now("menu_locale", 'extended');
 
      else
 
        t = t .. '{#pad_large|' .. translate_now("menu_locale", 'extended') .. '}';
 
      end;
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#pad_norm';
 
      act = function(s)
 
        prefs.text_padding = 1;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#pad_large';
 
      act = function(s)
 
        prefs.text_padding = 2;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#interval';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'interval'))));
 
      
 
      local t = '';
 
    
 
      if prefs.text_interval == 1 then
 
        t = t .. translate_now("menu_locale", 'normal') .. ' | ';
 
      else
 
        t = t .. '{#int_norm|' .. translate_now("menu_locale", 'normal') .. '} | ';
 
      end;
 
      
 
    
 
      if prefs.text_interval == 2 then
 
        t = t .. translate_now("menu_locale", 'extended');
 
      else
 
        t = t .. '{#int_large|' .. translate_now("menu_locale", 'extended') .. '}';
 
      end;
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#int_norm';
 
      act = function(s)
 
        prefs.text_interval = 1;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#int_large';
 
      act = function(s)
 
        prefs.text_interval = 2;
 
      
 
        prefs:store();
 
        set_theme(std.call(here(), 'theme'));
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#textinput';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'use_text_input'))));
 
      
 
      local t = '';
 
      if prefs.text_input then
 
        t = '{#ti_disable|' .. translate_now("menu_locale", 'disable') .. '} | ' .. translate_now("menu_locale", 'enable');
 
      else
 
        t = translate_now("menu_locale", 'disable') .. ' | {#ti_enable|' .. translate_now("menu_locale", 'enable') .. '}';
 
      end;
 
      
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#ti_disable';
 
      act = function(s)
 
        prefs.text_input = false;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#ti_enable';
 
      act = function(s)
 
        prefs.text_input = true;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
  };
 
  obj {
 
    nam = '#simplified_animation';
 
    dsc = function(s)
 
      pn(fmt.c(fmt.b(translate_now("menu_locale", 'simplified_animation'))));
 
      
 
      local t = '';
 
      if prefs.simplified_animation then
 
        t = '{#sa_disable|' .. translate_now("menu_locale", 'disable') .. '} | ' .. translate_now("menu_locale", 'enable');
 
      else
 
        t = translate_now("menu_locale", 'disable') .. ' | {#sa_enable|' .. translate_now("menu_locale", 'enable') .. '}';
 
      end;
 
      
 
      pn(fmt.c(t) .. '^');
 
    end;
 
  }: with {
 
    obj {
 
      nam = '#sa_disable';
 
      act = function(s)
 
        prefs.simplified_animation = false;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
    obj {
 
      nam = '#sa_enable';
 
      act = function(s)
 
        prefs.simplified_animation = true;
 
        prefs:store();
 
        walk(here());
 
      end;
 
    };
 
  };
 
}
terminal.lua
Show inline comments
 
deleted file
terminal/base.lua
Show inline comments
 
new file 100644
 
function terminal(s) 
 
  s[#s + 1] = {
 
    _keyts = 0;
 
    _key = 0;
 
    _keypress = false;
 
    path = 'C:\\';
 
    entry = '';
 
    noinv = true;
 
    status = 'command';
 
    theme = 'terminal';
 
    format_percent = 0;
 
    mx = false;
 
    my = false;
 
    directory_structure = {
 
      ['C:'] = {
 
        ['DOCS'] = {
 
          ['PASSWORD.TXT'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            else
 
              s:push(translate_now("terminal_locale", 'bad_command'));
 
            end;
 
          end;
 
          ['CRYPT.COM'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            elseif command == '' then
 
              if not args then
 
                s:push(translate_now("terminal_locale", 'crypt_usage'));
 
              else
 
                local new_args = {};
 
                for i in string.gmatch(args, "%S+") do
 
                  if i and i ~= '' then
 
                    new_args[#new_args + 1] = i;
 
                  end;
 
                end;
 

	
 
                if #new_args > 2 then
 
                  s:push(translate_now("terminal_locale", 'crypt_usage'));
 
                  return
 
                elseif #new_args < 2 then
 
                  s:push(translate_now("terminal_locale", 'crypt_key_unset'));
 
                  return;
 
                end;
 
                local name, path;
 
                name, path = s:walk_directory(new_args[1], true);
 
                
 
                if name ~= nil and path == 'C:\\DOCS\\PASSWORD.TXT' then
 
                  name, path = s:walk_directory(new_args[2], true);
 
                  if name ~= nil and path == 'C:\\DOCS\\KEY.TXT' then
 
                    s:push(translate_now("terminal_locale", 'crypt_key_locked'));
 
                    s.status = 'crypt';
 
                    return true;
 
                  else
 
                    s:push(translate_now("terminal_locale", 'crypt_invalid_key'));
 
                  end;
 
                  
 
                else
 
                  s:push(translate_now("terminal_locale", 'crypt_unencrypted'));
 
                end;
 
              end;
 
            end;
 
          end;
 
          ['CRYPT.CFG'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push('[MAIN]');
 
              s:push('ALGORITHM=AES');
 
              s:push('[RSA]');
 
              s:push('HARDWARE=FALSE');
 
              s:push(';KEY=KEY.TXT');
 
            else
 
              s:push(translate_now("terminal_locale", 'bad_command'));
 
            end;
 
          end;
 
          ['KEY.TXT'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            else
 
              s:push(translate_now("terminal_locale", 'bad_command'));
 
            end;
 
          end;
 
        },
 
        ['SYSTEM'] = {
 
          ['CONNECT.EXE'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            elseif command == '' then
 
              s.status = 'connect';
 
              return true;
 
            end;
 
          end;
 
          ['FORMAT.COM'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            elseif command == '' then
 
              if args and args:lower() == 'c:' then
 
                s.status = 'format';
 
                return true;
 
              elseif args and string.match(args, "^[A-z]:$") == args then
 
                s:push(translate_now("terminal_locale", 'format_invalid_device'));
 
              else
 
                s:push(translate_now("terminal_locale", 'format_usage'));
 
              end;
 
            end;
 
          end;
 
          ['HELP.COM'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            elseif command == '' then
 
              s:push(translate_now("terminal_locale", 'help_cd'));
 
              s:push(translate_now("terminal_locale", 'help_dir'));
 
              s:push(translate_now("terminal_locale", 'help_disk'));
 
              s:push(translate_now("terminal_locale", 'help_connect'));
 
              s:push(translate_now("terminal_locale", 'help_format'));
 
              s:push(translate_now("terminal_locale", 'help_help'));
 
              s:push(translate_now("terminal_locale", 'help_print'));
 
              s:push(translate_now("terminal_locale", 'help_exit'));
 
              s:push(translate_now("terminal_locale", 'help_com_exe'));
 
            end;
 
          end;
 
          ['PRINT.COM'] = function(s, command, args)
 
            if command == 'PRINT.COM' then
 
              s:push(translate_now("terminal_locale", 'binary_data'));
 
            elseif command == '' then
 
              if args then
 
                local cmd = s:walk_directory(args:upper(), true);
 
                if type(cmd) == 'function' then
 
                  cmd(s, 'PRINT.COM', '')
 
                else
 
                  s:push(translate_now("terminal_locale", 'print_no_file'));
 
                end;
 
              else
 
                s:push(translate_now("terminal_locale", 'print_usage'));
 
              end;
 
            end;
 
          end;
 
        },
 
        ['AUTOEXEC.BAT'] = function(s, command, args)
 
          if command == 'PRINT.COM' then
 
            s:push('SET PATH=C:\\SYSTEM');
 
            s:push(translate_now("terminal_locale", 'connect_comment'));
 
          elseif command == '' then
 
            s:push(translate_now("terminal_locale", 'bad_command'));
 
          end;
 
        end;
 
        ['CONFIG.SYS'] = function(s, command, args)
 
          if command == 'PRINT.COM' then
 
            s:push('DEVICE=C:\\SYSTEM\\HIMEM.SYS');
 
            s:push('DOS=HIGH,UMB');
 
            s:push('FILES=30');
 
            s:push('STACKS=0,0');
 
            s:push('BUFFERS=20');
 
            s:push('DEVICE=C:\\SYSTEM\\EMM386.EXE');
 
            s:push('DEVICE=C:\\SYSTEM\\TOWER.SYS');
 
          elseif command == '' then
 
            s:push(translate_now("terminal_locale", 'bad_command'));
 
          end;
 
        end;
 
        ['COMMAND.COM'] = function(s, command, args)
 
          if command == 'PRINT.COM' then
 
            s:push(translate_now("terminal_locale", 'binary_data'));
 
          elseif command == '' then
 
            s:push(translate_now("terminal_locale", 'command_already_started'));
 
          end;
 
        end;
 
        ['HELLO.TXT'] = function(s, command, args)
 
          if command == 'PRINT.COM' then
 
            s:push(translate_now("terminal_locale", 'hello_txt'));
 
          elseif command == '' then
 
            s:push(translate_now("terminal_locale", 'bad_command'));
 
          end;
 
        end;
 
      }
 
    };
 
  };
 
  
 
  s.disp = '';
 
  s.nofollow = true;
 
  s.nofading = true;
 
  
 
  s.themeInit = function(s)
 
    load_sounds('error_beep', 'keyboard1', 'keyboard2', 'keyboard3', 'keyboard4', 'keyboard5');
 
  end;
 
  
 
  s.query_formatter = function(s, path, entry)
 
    if s.status == 'command' then
 
      return s.path .. '>' .. s.entry .. '_';
 
    elseif s.status == 'crypt' or s.status == 'connect' then
 
      return translate_now("terminal_locale", 'query_password') .. entry .. '_';
 
    elseif s.status == 'format' then
 
      return translate_now("terminal_locale", 'query_type_yes') .. entry .. '_';
 
    elseif s.status == 'wait_format' then
 
      return translate_now("terminal_locale", 'query_formatting') .. " " .. tostring(s.format_percent) .. '%';
 
    elseif s.status == 'format_done' then
 
    end;
 
    return '';
 
  end;
 
  
 
  s.command_preprocessor = function(s, command)
 
    if s.status == 'crypt' then
 
      s.status = 'command';
 
      s:push(translate_now("terminal_locale", 'query_password') .. command);
 
      if command == password1 then
 
        s:push(password2);
 
        s:push('');
 
      else
 
        s:push(translate_now("terminal_locale", 'invalid_password'));
 
        s:push('');
 
      end;
 
      s.entry = '';
 
      return true;
 
    elseif s.status == 'connect' then
 
      s.entry = '';
 
      s.status = 'command';
 
      s:push(translate_now("terminal_locale", 'query_password') .. command);
 
      if command == password2 then
 
        if irradiated then
 
          snd.music('sound/bgm_ending_space_irradiated.ogg');
 
        else
 
          snd.music('sound/bgm_ending_space.ogg');
 
        end;
 
        if solarscale_with_me then
 
          walk('ending_solarscale_pre');
 
        else
 
          walk('ending_dissolving_tower_solarscale');
 
        end;
 
        
 
      else
 
        s:push(translate_now("terminal_locale", 'invalid_password_2'));
 
      end;
 
      
 
      return true;
 
    elseif s.status == 'format' then
 
      s.status = 'format';
 
      s.entry = '';
 
      s:push(translate_now("terminal_locale", 'query_type_yes') .. command);
 
      --s:push("Type 'Yes' to confirm:" .. command);
 
      if command:lower() == 'yes' then
 
        s.status = 'wait_format';
 
        if instead.tiny then
 
          _'#fail': enable();
 
        end;
 
        timer:set(25);
 
      else
 
        s:push(translate_now("terminal_locale", 'operation_cancelled'));
 
        s.status = 'command';
 
      end;
 
      
 
      return true;
 
    elseif s.status == 'wait_format' then
 
      s.entry = '';
 
      return true;
 
    end;
 
  end;
 
  
 
  s.puch_cmd = function(s, text)
 
    s:push_line(text);
 
  end;
 
  
 
  s.push = function(s, text)
 
    local lines = split(text, '^');
 
    
 
    for _, l in ipairs(lines) do
 
      s:push_line(l);
 
    end;
 
  end;
 
  
 
  local oldenter = s.onenter;
 
  
 
  s.onenter = function(s)
 
    s.path = 'C:\\';
 
    s.entry = '';
 
    
 
    if oldenter then
 
      s:oldenter();
 
    end;
 
    
 
    s:process_directory_structure(s.directory_structure['C:']);
 
    s:push(translate_now("terminal_locale", 'tower_os_line_1'));
 
    s:push('');
 
    s:push(translate_now("terminal_locale", 'tower_os_line_2'));
 
    s:push('');
 
    s:push(translate_now("terminal_locale", 'tower_os_line_3'));
 
    s:push('');
 

	
 
    s:makekeyboard();
 
    
 
    instead.autosave();
 
  end;
 
  
 
  s.process_command = function(s, str)
 
    s:puch_cmd(s.path .. '>' .. str);
 
    
 
    local rval = {}
 
    
 
    for i in string.gmatch(str, "%S+") do
 
      if (#rval > 0) then
 
        if rval[2] then
 
          rval[2] = rval[2] .. ' ' .. i;
 
        else
 
          rval[2] = i;
 
        end;
 
      else
 
        rval[1] = i;
 
      end;
 
      
 
    end;
 
    
 
    return rval;
 
  end;
 
  
 
  s.process_directory_structure = function(s, current, parent)
 
    current['.'] = current;
 
    current['..'] = parent;
 
    for k,v in pairs(current) do
 
      if k ~= '.' and k ~= '..' and type(v) == 'table' then
 
        s:process_directory_structure(v, current);
 
      end;
 
    end;
 
  end;
 
  
 
  s.walk_directory = function(s, path, no_replace)
 
    local location = s.directory_structure;
 
    
 
    if path:sub(1, 2):upper() ~= 'C:' then
 
      path = s.path .. path;
 
    end;
 
    
 
    local spath = {};
 
    
 
    for part in string.gmatch(path, "[^\\\\]+") do
 
      part = part:upper();
 
      if part == '.' then
 
      elseif part == '..' then
 
        spath[#spath] = nil;
 
      else
 
        spath[#spath + 1] = part;
 
      end;
 
      local oloc = location;
 
      location = location[part];
 
      if not no_replace then
 
        if location == nil then
 
          location = oloc[part .. '.COM'];
 
        end;
 
        if location == nil then
 
          location = oloc[part .. '.EXE'];
 
        end;
 
      end;
 

	
 
      if location == nil then
 
        return nil, nil
 
      end;
 
    end;
 
    return location, cat_list(spath, '\\');
 
  end;
 
  
 
  s.call_command = function(s, command, args)
 
    local r = false;
 
    if command == '' or not command then
 
      return
 
    end;
 
    
 
    command = command:lower();
 
    
 
    if command == 'dir' then
 
      local rval = {};
 
    
 
      local subdir = s:walk_directory(s.path);
 
       local sort = function(s1, s2)
 
         if type(subdir[s1]) == 'function' and type(subdir[s2]) == 'table' then
 
           return false;
 
         elseif type(subdir[s1]) == 'table' and type(subdir[s2]) == 'function' then
 
           return true;
 
         end;
 
         return s1 < s2;
 
      end;
 
      
 
      for k, v in pairs(subdir) do
 
        rval[#rval + 1] = k;
 
      end;
 
      
 
      table.sort(rval, sort);
 
      
 
      for i, v in ipairs(rval) do
 
        s:push(v);
 
      end;
 
    elseif command == 'cd' then
 
      if args then
 
        args = args:upper();
 
        local dir, path = s:walk_directory(args)
 
        if type(dir) == 'table' then
 
          s.path = path .. '\\';
 
        else
 
          s:push(translate_now("terminal_locale", 'cd_invalid_directory', {path=args}));
 
        end;
 
      else
 
        s:push(translate_now("terminal_locale", 'cd_usage'));
 
      end;
 
    elseif command == 'ver' then
 
      s:push(translate_now("terminal_locale", 'tower_os_line_2'));
 
    elseif command == 'exit' then
 
      walk('tower_level_4_room_2');
 
      return true;
 
    elseif command == 'rm' and args and #args >= 2 and (args == '-rf . /' or args == '-rf /') then
 
      s:push(translate_now("terminal_locale", random_phrase({'unix_line_3', 'unix_line_4'})));
 
    elseif command == 'sudo' or command == 'su' or command == 'ls' or command == 'rm' then
 
      s:push(translate_now("terminal_locale", random_phrase({'unix_line_1', 'unix_line_2'})));
 
    elseif command == 'cat' then
 
      s:push(translate_now("terminal_locale", random_phrase({'cat_line_1', 'cat_line_2', 'cat_line_3'})));
 
    elseif command == 'more' or command == 'less' then
 
      s:push(translate_now("terminal_locale", 'more_line_1'));
 
    elseif command == 'head' or command == 'tail' then
 
      s:push(translate_now("terminal_locale", random_phrase({'head_line_1', 'head_line_2'})));
 
    elseif command == 'echo' then
 
      if args then
 
        s:push(args);
 
      else
 
        s:push('');
 
      end;
 
    elseif command == 'help' or command:lower() == 'help.com' then
 
      r = s.directory_structure['C:']['SYSTEM']['HELP.COM'](s, '', args);
 
    elseif command == 'print' or command:lower() == 'print.com' then
 
      r = s.directory_structure['C:']['SYSTEM']['PRINT.COM'](s, '', args);
 
    elseif command == 'format' or command:lower() == 'format.com' then
 
      r = s.directory_structure['C:']['SYSTEM']['FORMAT.COM'](s, '', args);
 
    elseif command == 'connect' or command:lower() == 'connect.exe' then
 
      r = s.directory_structure['C:']['SYSTEM']['CONNECT.EXE'](s, '', args);
 
    elseif #command == 2 and command:sub(2, 2) == ':' then
 
      if (s.directory_structure[command]) then
 
        s.path = command:upper() .. '\\';
 
      else
 
        s:push(translate_now("terminal_locale", 'format_invalid_device'));
 
      end;
 
    else
 
      local cmd = s:walk_directory(command:upper())
 
      if type(cmd) == 'function' then
 
        r = cmd(s, '', args);
 
      else
 
        s:push(translate_now("terminal_locale", 'bad_command'));
 
      end;
 
    end;
 
    if not r then
 
      s:push('');
 
    end;
 
  end;
 
  
 
  return room(s);
 
end;
 
\ No newline at end of file
terminal/common.lua
Show inline comments
 
new file 100644
 
if instead.tiny then
 
  include "terminal/tiny.lua"
 
else
 
  include "terminal/full.lua"
 
end;
 

	
 
function make_password()
 
  local words = {
 
    'Alpha';
 
    'Ancient';
 
    'Beta';
 
    'Biography';
 
    'Clear';
 
    'Catalyst';
 
    'Delta';
 
    'Destination';
 
    'Endless';
 
    'Esoteric';
 
    'Flower';
 
    'Freedom';
 
    'Gamma';
 
    'Geography';
 
    'Home';
 
    'History';
 
    'Infinite';
 
    'Initial';
 
    'Instead';
 
    'Journey';
 
    'King';
 
    'Knight';
 
    'Knowledge';
 
    'Lion';
 
    'Lost';
 
    'Magic';
 
    'Micron';
 
    'Night';
 
    'Novel';
 
    'Omega';
 
    'Ocean';
 
    'Precise';
 
    'Power';
 
    'Quality';
 
    'Question';
 
    'Rarity';
 
    'Reason';
 
    'Silent';
 
    'Secret';
 
    'Tiger';
 
    'Theta';
 
    'Tower';
 
    'Ultimate';
 
    'Universe';
 
    'Visitor';
 
    'Vital';
 
    'Wolf';
 
    'Wizardry';
 
  };
 
  local numbers = {'0','1','2','3','4','5','6','7','8','9'};
 
  
 
  return random_phrase(words) .. '_' .. random_phrase(words) .. '_' .. random_phrase(numbers) .. random_phrase(numbers);
 
end;

Changeset was too big and was cut off... Show full diff