Games-Construder

 view release on metacpan or  search on metacpan

lib/Games/Construder/Server/UI.pm  view on Meta::CPAN

# Games::Construder - A 3D Game written in Perl with an infinite and modifiable world.
# Copyright (C) 2011  Robin Redeker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
package Games::Construder::Server::UI;
use common::sense;
use Scalar::Util qw/weaken/;
require Exporter;
our @ISA = qw/Exporter/;
our @EXPORT = qw/
   ui_player_score
   ui_player_bio_warning
   ui_player_tagger
/;

=head1 NAME

Games::Construder::Server::UI - Server-side Userinterface for Player interaction

=over 4

=cut

sub new {
   my $this  = shift;
   my $class = ref ($this) || $this;
   my $self  = { @_ };
   bless $self, $class;

   weaken $self->{pl};

   $self->init;

   return $self
}

sub init {
}

sub layout {
   my ($self, @args) = @_;
   die "subclass responsibility\n";
}

sub commands { # subclasses should overwrite this
   my ($self) = @_;
   # key => cmd name
   ()
}

sub new_ui {
   my ($self, @args) = @_;
   $self->{pl}->new_ui (@args);
}

sub show_ui {
   my ($self, $name, @arg) = @_;
   $self->{pl}->{uis}->{$name}->show (@arg);
}

sub hide_ui {
   my ($self, $name) = @_;
   $self->{pl}->{uis}->{$name}->hide;
}

sub delete_ui {
   my ($self, @args) = @_;
   $self->{pl}->delete_ui (@args);
}

sub update {
   my ($self, @args) = @_;
   $self->show (@args) if $self->{shown};
}

sub show {
   my ($self, @args) = @_;
   my $lyout = $self->layout (@args);
   $lyout->{commands}->{default_keys} = { $self->commands };
   if ($self->{cmd_need_select_boxes}) {
      $lyout->{commands}->{need_selected_boxes} = 1;
   }
   $self->{pl}->display_ui ($self->{ui_name} => $lyout);
}

sub handle_command { # subclasses should overwrite this
}

sub react {
   my ($self, $cmd, $arg, $pos) = @_;
   return unless $self->{shown};

   $self->handle_command ($cmd, $arg, $pos);
}

sub hide {
   my ($self) = @_;
   $self->{pl}->display_ui ($self->{ui_name});
}

sub DESTROY {
   my ($self) = @_;
   $self->{pl}->display_ui ($self->{ui_name});
}

package Games::Construder::Server::UI::Score;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub layout {
   my ($self, $hl) = @_;

   if ($hl) {
      my $wself = $self;
      weaken $wself;
      $self->{upd_score_hl_tmout} = AE::timer 1.5, 0, sub {
         $wself->show;
         delete $wself->{upd_score_hl_tmout};
      };
   }

   my $score =  $self->{pl}->{data}->{score};

   ui_hud_window ([center => "up"],
      [box => {
            border  => { color => $hl ? "#ff0000" : "#777700" },
            padding => ($hl ? 10 : 2),
            dir => "vert",
            },
         ($self->{pl}->{data}->{cheating}
            ? [text => { color => "#ff0000", font => "small", align => "center" },
               "<cheating>"]
            : ()),
         [box => {
               dir   => "hor",
               align => "center"
          },
          [text => {
             font  => "normal",
             color => "#aa8800",
             align => "center"
           }, "Score:"],
          [text => {
             font  => "big",
             color => $hl ? "#ff0000" : "#aa8800",
           }, ($score . ($hl ? ($hl > 0 ? "+$hl" : "$hl") : ""))],
         ],
      ]
   )
}

package Games::Construder::Server::UI::BioWarning;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub layout {
   my ($self, $seconds) = @_;

   ui_hud_window_transparent (
      [center => 'center', 0, -0.15],
      ui_warning (
          "Warning: Bio energy level low! You have $seconds seconds left!"
      ),
      ui_subdesc (
       "Death imminent, please dematerialize something that provides bio energy!",
      )
   )
}

package Games::Construder::Server::UI::ProximityWarning;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub commands {
}

sub handle_command {
   my ($self, $cmd) = @_;

}

sub layout {
   my ($self, $msg) = @_;

   my $wself = $self;
   weaken $wself;
   $self->{msg_tout} = AE::timer (3, 0, sub {
      $wself->hide;
      delete $wself->{msg_tout};
   });

   ui_hud_window_transparent (
      [center => "center", -0.25],
      ui_warning ($msg)
   );
}

package Games::Construder::Server::UI::MsgBox;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub layout {
   my ($self, $error, $msg) = @_;

   my $wself = $self;
   weaken $wself;
   $self->{msg_tout} = AE::timer (($error ? 7 : 3), 0, sub {
      $wself->hide;
      delete $wself->{msg_tout};
   });

   ui_hud_window_transparent (
      [center => "center", 0, 0.15],
      $error
         ? ui_warning ($msg)
         : ui_notice ($msg)
   )
}

package Games::Construder::Server::UI::Slots;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub commands {
   (
      map {
         $_ => "slot_" . ($_ == 0 ? 9 : $_ - 1)
      } 0..9
   )
}

sub handle_command {
   my ($self, $cmd, $arg, $pos) = @_;

   if ($cmd =~ /slot_(\d+)/) {
      $self->{pl}->{data}->{slots}->{selected} = $1;
      $self->show;
   }
}

sub layout {
   my ($self) = @_;

   my $slots = $self->{pl}->{data}->{slots};

   my @slots;
   my $selected_type;
   for (my $i = 0; $i < 10; $i++) {
      my $invid = $slots->{selection}->[$i];

      my ($cnt) = $self->{pl}->{inv}->get_count ($invid);
      if ($invid =~ /:/ && $cnt == 0) {
         $slots->{selection}->[$i] = undef;
         $invid = undef;
      }

      my ($type, $invid) = $self->{pl}->{inv}->split_invid ($invid);
      if ($slots->{selected} == $i) {
         $selected_type = $type;
      }

      push @slots,
         ui_hlt_border (($i == $slots->{selected}),
            [box => { padding => 2, align => "center" },
              [model => { color => "#00ff00", width => 30 }, $type]],

lib/Games/Construder/Server/UI.pm  view on Meta::CPAN

   my $obj =
      $Games::Construder::Server::RES->get_object_by_type ($selected_type)
         if $selected_type;

   ui_hud_window ([left => "down"],
      $obj ? (ui_small_text ("Selected: " . $obj->{name})) : (),
      [box => { dir => "hor" }, @{$grid[0]}],
      [box => { dir => "hor" }, @{$grid[1]}]
   )
}

package Games::Construder::Server::UI::Help;
use Games::Construder::UI;
use base qw/Games::Construder::Server::UI/;

sub layout {
   ui_window ("Server Handled Keybindings",
      ui_desc ("Global Bindings:"),
      ui_key_explain ("left mouse btn",  "Materialize block from selected slot."),
      ui_key_explain ("right mouse btn", "Dematerialize block."),
      ui_key_explain ("q",               "Query information for highlighted block."),
      ui_key_explain ("e",               "Interact with highlighted block."),
      ui_key_explain ("i",               "Open inventory."),
      ui_key_explain ("n",               "Open navigator programmer."),
      ui_key_explain ("m",               "Toggle navigator visibility."),
      ui_key_explain ("x",               "Open assignment information."),
      ui_key_explain ("o",               "Open notebook."),
      ui_key_explain ("b",               "Open material handbook."),
      ui_key_explain ("r",               "Open color selector."),
      ui_key_explain ("l",               "Create encounter (developer stuff)."),
      ui_key_explain ("h",               "Cheat."),
      ui_key_explain ("F3",              "Displays this help screen."),
      ui_key_explain ("F4",              "Opens your trophy overview."),
      ui_key_explain ("F7",              "Display Server Information."),
      ui_key_explain ("F8",              "Commit suicide, when you want to start over."),
   )
}

package Games::Construder::Server::UI::ServerInfo;
use Games::Construder::UI;

use base qw/Games::Construder::Server::UI/;

sub layout {
   ui_window ("Server Info",
      ui_text ("Server Map Directory: $Games::Construder::Server::Resources::MAPDIR"),
      ui_text ("Server Player Directory: $Games::Construder::Server::Resources::PLAYERDIR"),
   )
}

package Games::Construder::Server::UI::Status;
use Games::Construder::UI;
use Games::Construder::Server::World;

use base qw/Games::Construder::Server::UI/;

sub init {
   my ($self) = @_;

   my $wself = $self;
   weaken $wself;
   $self->{tmr} = AE::timer 0, 1, sub {
      $wself->show;
   };
}

sub commands {
   my ($self) = @_;
   $self->{cmd_need_select_boxes} = 1;

   (
      f2  => "menu",
      f3  => "help",
      f4  => "trophies",
      f7  => "server_info",
      f8  => "kill",
      f11 => "text_script",
      f10 => "text_script_hide",
      f12 => "exit_server",
      i   => "inventory",
      n   => "navigation_programmer",
      m   => "toggle_navigator",
      h   => "cheat",
      x   => "assignment",
      t   => "location_book",
      e   => "interact",
      q   => "query",
      o   => "notebook",
      b   => "material_handbook",
      r   => "color_select",
      l   => "encounter"
   )
}

sub handle_command {
   my ($self, $cmd, $arg, $pos) = @_;

   my $pl = $self->{pl};

   if ($cmd eq 'inventory') {
      $self->show_ui ('inventory');
   } elsif ($cmd eq 'location_book') {
      $pl->show_location_book;
   } elsif ($cmd eq 'navigation_programmer') {
      $self->show_ui ('navigation_programmer');
   } elsif ($cmd eq 'cheat') {
      $self->show_ui ('cheat');
   } elsif ($cmd eq 'contact') {
      $self->show_ui ('ship_transmission');
   } elsif ($cmd eq 'interact') {
      $pl->interact ($pos->[0]) if @{$pos->[0] || []};
   } elsif ($cmd eq 'query') {
      $pl->query ($pos->[0]);
   } elsif ($cmd eq 'assignment') {
      $self->show_ui ('assignment');
   } elsif ($cmd eq 'material_handbook') {
      $self->show_ui ('material_handbook');
   } elsif ($cmd eq 'notebook') {
      $self->show_ui ('notebook');
   } elsif ($cmd eq 'color_select') {
      $self->show_ui ('color_select');

lib/Games/Construder/Server/UI.pm  view on Meta::CPAN

      $self->hide;
      $self->show_ui ('kill_player');

   } elsif ($cmd eq 'help' || $cmd eq 'menu') {
      $self->show_ui ('help');
   } elsif ($cmd eq 'toggle_navigator') {
      if ($self->{pl}->{uis}->{navigator}->{shown}) {
         $self->hide_ui ('navigator');
      } else {
         $self->show_ui ('navigator');
      }
   } elsif ($cmd eq 'trophies') {
      $self->show_ui ("trophies");
   } elsif ($cmd eq 'exit_server') {
      $self->new_ui (shutdown =>
         "Games::Construder::Server::UI::ConfirmQuery",
         msg       => "Do you really want to shutdown the server?",
         cb => sub {
            $self->delete_ui ('shutdown');
            if ($_[0]) {
               $self->{pl}->msg (0, "Shutting down the server in 2 seconds...");
               my $t; $t = AE::timer 2, 0, sub {
                  $Games::Construder::Server::World::SRV->shutdown;
                  undef $t;
               };
            }
         });
      $self->hide;
      $self->show_ui ('shutdown');
   }
}

sub _range_color {
   my ($perc, $low_ok) = @_;
   my ($first, $second) = (
      int (($low_ok / 2) / 10) * 10,
      $low_ok
   );

     $perc < $first  ? "#ff5555"
   : $perc < $second ? "#ffff55"
   : "#55ff55"
}

sub time2str {
   my $m = int ($_[0] / 60);
   sprintf "%2dm %2ds", $m, $_[0] - ($m * 60)
}

sub layout {
   my ($self, $bio_usage) = @_;

   my $abs_pos  = $self->{pl}->get_pos_normalized;
   my $chnk_pos = $self->{pl}->get_pos_chnk;
   my $sec_pos  = $self->{pl}->get_pos_sector;

   my $sinfo = world_sector_info ($chnk_pos);

   if (ref $bio_usage) {
      my $wself = $self;
      weaken $wself;
      $self->{bio_intake} = $bio_usage;
      $self->{bio_intake_tmr} = AE::timer 2, 0, sub {
         delete $wself->{bio_intake};
         $wself->show;
         delete $wself->{bio_intake_tmr};
      };

   } elsif ($bio_usage) {
      my $wself = $self;
      weaken $wself;
      $self->{bio_usage} = $bio_usage;
      $self->{bio_usage_tmr} = AE::timer 2, 0, sub {
         delete $wself->{bio_usage};
         $wself->show;
         delete $wself->{bio_usage_tmr};
      };
   }

   ui_hud_window (
     [right => 'up'],
     ui_border (
        [box => { dir => "hor" },
           [box => { dir => "vert", padding => 2 },
              [text => { color => "#FFFF88", font => "small" }, "Time"],
              [text => { color => "#888888", font => "small" }, "Pos"],
              #d#[text => { color => "#888888", font => "small" }, "Look"],
              [text => { color => "#888888", font => "small" }, "Chunk"],
              [text => { color => "#888888", font => "small" }, "Sector"],
              [text => { color => "#888888", font => "small" }, "Type"],
           ],
           [box => { dir => "vert", padding => 2 },
              [text => { color => "#ffffff", font => "small" },
                 time2str ($self->{pl}->{data}->{time})],
              [text => { color => "#ffffff", font => "small" },
                 sprintf ("%3d,%3d,%3d", @$abs_pos)],
              #d#[text => { color => "#ffffff", font => "small" },
              #d#   sprintf ("%3d,%3d,%3d", @{vsmul ($self->{data}->{look_vec}, 10)})],
              [text => { color => "#ffffff", font => "small" },
                 sprintf ("%3d,%3d,%3d", @$chnk_pos)],
              [text => { color => "#ffffff", font => "small" },
                 sprintf ("%3d,%3d,%3d", @$sec_pos)],
              [text => { color => "#ffffff", font => "small" },
                 sprintf ("%s, %0.5f", $sinfo->{type}, $sinfo->{param})],
           ]
        ],
        [box => { },
           [text => { align => "right", font => "big", color => _range_color ($self->{pl}->{data}->{happyness}, 90), max_chars => 4 },
              sprintf ("%d%%", $self->{pl}->{data}->{happyness})],
           [text => { align => "center", color => "#888888" }, "happy"],
        ],
        [box => { },
           [text => { align => "right", font => "big", color => _range_color ($self->{pl}->{data}->{bio}, 60), max_chars => 4 },
              sprintf ("%d%%", $self->{pl}->{data}->{bio})],
           ($self->{bio_intake}
              ? [box => { dir => "hor", align => "left" },
                   [text => { align => "center", font => "big", color => "#00ff00", wrap => -2 }, "+"],
                   [model => { animated => 0, align => "center", width => 30 }, $self->{bio_intake}->[0]]]
              : [text => { align => "center", color => "#888888" }, "bio"])
        ],
        ($self->{bio_usage}
           ? [box => { },
              [text => { align => "center", color => "#FFaa00" }, "-$self->{bio_usage}% bio"]]
           : ()),
        [box => { },
           [text => { align => "right",
                      color => $self->{pl}->{data}->{signal_jammed}
                         ? "#00ff00" : "#ff0000" },
              $self->{pl}->{data}->{signal_jammed} ? "Jammed" : "Clear"],
           [text => { align => "center", color => "#888888" }, " signal"],
        ],



( run in 2.081 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )