<commit_before>/* Copyright (c) 2008-2017 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * MRtrix 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.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include <unsupported/Eigen/FFT>

#include "command.h"
#include "progressbar.h"
#include "image.h"
#include "algo/threaded_loop.h"
#include <numeric>

using namespace MR;
using namespace App;

void usage ()
{
  AUTHOR = "Ben Jeurissen (ben.jeurissen@uantwerpen.be) & J-Donald Tournier (jdtournier@gmail.com)";

  SYNOPSIS = "Remove Gibbs Ringing Artifact";

  DESCRIPTION
    + "This application attempts to remove Gibbs ringing artefacts from MRI images using the method "
    "of local subvoxel-shifts proposed by Kellner et al. (see reference below for details).";

  ARGUMENTS
  + Argument ("in", "the input image.").type_image_in ()
  + Argument ("out", "the output image.").type_image_out ();

  OPTIONS
  + Option ("axes",
            "select the slice axes (default: 0,1 - i.e. x-y).")
  +   Argument ("list").type_sequence_int ()

  + Option ("nshifts", "discretization of subpixel spacing (default: 20).")
  +   Argument ("value").type_integer (8, 128)

  + Option ("minW", "left border of window used for TV computation (default: 1).")
  +   Argument ("value").type_integer (0, 10)

  + Option ("maxW", "right border of window used for TV computation (default: 3).")
  +   Argument ("value").type_integer (0, 128)

  + DataType::options();

  REFERENCES
    + "Kellner, E; Dhital, B; Kiselev, V.G & Reisert, M. "
    "Gibbs-ringing artifact removal based on local subvoxel-shifts. "
    "Magnetic Resonance in Medicine, 2016, 76, 1574–1581.";

}

typedef double value_type;

class ComputeSlice
{ MEMALIGN (ComputeSlice)
  public:
    ComputeSlice (const vector<size_t>& outer_axes, const vector<size_t>& slice_axes, const int& nsh, const int& minW, const int& maxW, Image<value_type>& in, Image<value_type>& out) :
      outer_axes (outer_axes),
      slice_axes (slice_axes),
      nsh (nsh),
      minW (minW),
      maxW (maxW),
      in (in),
      out (out),
      im1 (in.size(slice_axes[0]), in.size(slice_axes[1])),
      im2 (im1.rows(), im1.cols()) {
        prealloc_FFT();
      }

    ComputeSlice (const ComputeSlice& other) :
      outer_axes (other.outer_axes),
      slice_axes (other.slice_axes),
      nsh (other.nsh),
      minW (other.minW),
      maxW (other.maxW),
      in (other.in),
      out (other.out),
      fft (),
      im1 (in.size(slice_axes[0]), in.size(slice_axes[1])),
      im2 (im1.rows(), im1.cols()) {
        prealloc_FFT();
      }

    void operator() (const Iterator& pos)
    {
      const int X = slice_axes[0];
      const int Y = slice_axes[1];
      assign_pos_of (pos, outer_axes).to (in, out);

      for (auto l = Loop (slice_axes) (in); l; ++l)
        im1 (in.index(X), in.index(Y)) = cdouble (in.value(), 0.0);

      unring_2d ();

      for (auto l = Loop (slice_axes) (out); l; ++l)
        out.value() = im1 (out.index(X), out.index(Y)).real();
    }

  private:
    const vector<size_t>& outer_axes;
    const vector<size_t>& slice_axes;
    const int nsh, minW, maxW;
    Image<value_type> in, out;
    Eigen::FFT<double> fft;
    Eigen::MatrixXcd im1, im2, shifted;
    Eigen::VectorXcd v;

    void prealloc_FFT () {
      // needed to avoid within-thread allocations,
      // which aren't thread-safe in FFTW:
#ifdef EIGEN_FFTW_DEFAULT
      Eigen::VectorXcd tmp (im1.rows());
      FFT (tmp);
      iFFT (tmp);
      tmp.resize (im1.cols());
      FFT (tmp);
      iFFT (tmp);
#endif
    }

    template <typename Derived> FORCE_INLINE void FFT      (Eigen::MatrixBase<Derived>&& vec) { fft.fwd (v, vec); vec = v; }
    template <typename Derived> FORCE_INLINE void FFT      (Eigen::MatrixBase<Derived>& vec) { FFT (std::move (vec)); }
    template <typename Derived> FORCE_INLINE void iFFT     (Eigen::MatrixBase<Derived>&& vec) { fft.inv (v, vec); vec = v; }
    template <typename Derived> FORCE_INLINE void iFFT     (Eigen::MatrixBase<Derived>& vec) { iFFT (std::move (vec)); }
    template <typename Derived> FORCE_INLINE void row_FFT  (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.rows(); ++n)  FFT (mat.row(n)); }
    template <typename Derived> FORCE_INLINE void row_iFFT (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.rows(); ++n) iFFT (mat.row(n)); }
    template <typename Derived> FORCE_INLINE void col_FFT  (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.cols(); ++n)  FFT (mat.col(n)); }
    template <typename Derived> FORCE_INLINE void col_iFFT (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.cols(); ++n) iFFT (mat.col(n)); }

    FORCE_INLINE void unring_2d ()
    {
      row_FFT (im1);
      col_FFT (im1);

      for (int k = 0; k < im1.cols(); k++) {
        double ck = (1.0+cos(2.0*Math::pi*(double(k)/im1.cols())))*0.5;
        for (int j = 0 ; j < im1.rows(); j++) {
          double cj = (1.0+cos(2.0*Math::pi*(double(j)/im1.rows())))*0.5;

          if (ck+cj != 0.0) {
            im2(j,k) = im1(j,k) * cj / (ck+cj);
            im1(j,k) *= ck / (ck+cj);
          }
          else
            im1(j,k) = im2(j,k) = cdouble(0.0, 0.0);
        }
      }

      row_iFFT (im1);
      col_iFFT (im2);

      unring_1d (im1);
      unring_1d (im2.transpose());

      im1 += im2;
    }

    template <typename Derived>
      FORCE_INLINE void unring_1d (Eigen::MatrixBase<Derived>&& eig)
      {
        const int n = eig.rows();
        const int numlines = eig.cols();
        shifted.resize (n, 2*nsh+1);

        int shifts [2*nsh+1];
        shifts[0] = 0;
        for (int j = 0; j < nsh; j++) {
          shifts[j+1] = j+1;
          shifts[1+nsh+j] = -(j+1);
        }

        double TV1arr[2*nsh+1];
        double TV2arr[2*nsh+1];

        for (int k = 0; k < numlines; k++) {
          shifted.col(0) = eig.col(k);

          const int maxn = (n&1) ? (n-1)/2 : n/2-1;

          for (int j = 1; j < 2*nsh+1; j++) {
            double phi = Math::pi*double(shifts[j])/double(n*nsh);
            cdouble u (std::cos(phi), std::sin(phi));
            cdouble e (1.0, 0.0);
            shifted(0,j) = shifted(0,0);

            if (!(n&1))
              shifted(n/2,j) = cdouble(0.0, 0.0);

            for (int l = 0; l < maxn; l++) {
              e = u*e;
              int L = l+1; shifted(L,j) = e * shifted(L,0);
              L = n-1-l;   shifted(L,j) = std::conj(e) * shifted(L,0);
            }
          }

          col_iFFT (shifted);

          for (int j = 0; j < 2*nsh+1; ++j) {
            TV1arr[j] = 0.0;
            TV2arr[j] = 0.0;
            for (int t = minW; t <= maxW; t++) {
              TV1arr[j] += std::abs (shifted((n-t)%n,j).real() - shifted((n-t-1)%n,j).real());
              TV1arr[j] += std::abs (shifted((n-t)%n,j).imag() - shifted((n-t-1)%n,j).imag());
              TV2arr[j] += std::abs (shifted((n+t)%n,j).real() - shifted((n+t+1)%n,j).real());
              TV2arr[j] += std::abs (shifted((n+t)%n,j).imag() - shifted((n+t+1)%n,j).imag());
            }
          }

          for (int l = 0; l < n; ++l) {
            double minTV = std::numeric_limits<double>::max();
            int minidx = 0;
            for (int j = 0; j < 2*nsh+1; ++j) {

              if (TV1arr[j] < minTV) {
                minTV = TV1arr[j];
                minidx = j;
              }
              if (TV2arr[j] < minTV) {
                minTV = TV2arr[j];
                minidx = j;
              }

              TV1arr[j] += std::abs (shifted((l-minW+1+n)%n,j).real() - shifted((l-(minW  )+n)%n,j).real());
              TV1arr[j] -= std::abs (shifted((l-maxW  +n)%n,j).real() - shifted((l-(maxW+1)+n)%n,j).real());
              TV2arr[j] += std::abs (shifted((l+maxW+1+n)%n,j).real() - shifted((l+(maxW+2)+n)%n,j).real());
              TV2arr[j] -= std::abs (shifted((l+minW  +n)%n,j).real() - shifted((l+(minW+1)+n)%n,j).real());

              TV1arr[j] += std::abs (shifted((l-minW+1+n)%n,j).imag() - shifted((l-(minW  )+n)%n,j).imag());
              TV1arr[j] -= std::abs (shifted((l-maxW  +n)%n,j).imag() - shifted((l-(maxW+1)+n)%n,j).imag());
              TV2arr[j] += std::abs (shifted((l+maxW+1+n)%n,j).imag() - shifted((l+(maxW+2)+n)%n,j).imag());
              TV2arr[j] -= std::abs (shifted((l+minW  +n)%n,j).imag() - shifted((l+(minW+1)+n)%n,j).imag());
            }

            double a0r = shifted((l-1+n)%n,minidx).real();
            double a1r = shifted(l,minidx).real();
            double a2r = shifted((l+1+n)%n,minidx).real();
            double a0i = shifted((l-1+n)%n,minidx).imag();
            double a1i = shifted(l,minidx).imag();
            double a2i = shifted((l+1+n)%n,minidx).imag();
            double s = double(shifts[minidx])/(2.0*nsh);

            if (s > 0.0)
              eig(l,k) = cdouble (a1r*(1.0-s) + a0r*s, a1i*(1.0-s) + a0i*s);
            else
              eig(l,k) = cdouble (a1r*(1.0+s) - a2r*s, a1i*(1.0+s) - a2i*s);
          }
        }
      }

    template <typename Derived>
      FORCE_INLINE void unring_1d (Eigen::MatrixBase<Derived>& eig) { unring_1d (std::move (eig)); }

};

void run ()
{
  const int nshifts = App::get_option_value ("nshifts", 20);
  const int minW = App::get_option_value ("minW", 1);
  const int maxW = App::get_option_value ("maxW", 3);

  if (minW >= maxW)
    throw Exception ("minW must be smaller than maxW");

  auto in = Image<value_type>::open (argument[0]);
  Header header (in);

  header.datatype() = DataType::from_command_line (DataType::Float32);
  auto out = Image<value_type>::create (argument[1], header);

  vector<size_t> slice_axes = { 0, 1 };
  auto opt = get_options ("axes");
  if (opt.size()) {
    vector<int> axes = opt[0][0];
    if (slice_axes.size() != 2)
      throw Exception ("slice axes must be specified as a comma-separated 2-vector");
    slice_axes = { size_t(axes[0]), size_t(axes[1]) };
  }

  // build vector of outer axes:
  vector<size_t> outer_axes (header.ndim());
  std::iota (outer_axes.begin(), outer_axes.end(), 0);
  for (const auto axis : slice_axes) {
    auto it = std::find (outer_axes.begin(), outer_axes.end(), axis);
    if (it == outer_axes.end())
      throw Exception ("slice axis out of range!");
    outer_axes.erase (it);
  }

  ThreadedLoop ("performing Gibbs ringing removal", in, outer_axes, slice_axes)
    .run_outer (ComputeSlice (outer_axes, slice_axes, nshifts, minW, maxW, in, out));
}

<commit_msg>mrdegibbs: additional documentation about partial Fourier and motion correction<commit_after>/* Copyright (c) 2008-2017 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * MRtrix 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.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include <unsupported/Eigen/FFT>

#include "command.h"
#include "progressbar.h"
#include "image.h"
#include "algo/threaded_loop.h"
#include <numeric>

using namespace MR;
using namespace App;

void usage ()
{
  AUTHOR = "Ben Jeurissen (ben.jeurissen@uantwerpen.be) & J-Donald Tournier (jdtournier@gmail.com)";

  SYNOPSIS = "Remove Gibbs Ringing Artifacts";

  DESCRIPTION
    + "This application attempts to remove Gibbs ringing artefacts from MRI images using the method "
    "of local subvoxel-shifts proposed by Kellner et al. (see reference below for details)."

    + "This command is designed to run on data directly after it has been reconstructed by the scanner, "
    "before any interpolation of any kind has taken place. You should not run this command after any "
    "form of motion correction (e.g. not after dwipreproc). Similarly, if you intend running dwidenoise, "
    "you should run this command afterwards, since it has the potential to alter the noise structure, "
    "which would impact on dwidenoise's performance."

    + "Note that this method is designed to work on images acquired with full k-space coverage. "
    "Running this method on partial Fourier ('half-scan') data may lead to suboptimal and/or biased "
    "results, as noted in the original reference below. There is currently no means of dealing with this; "
    "users should exercise caution when using this method on partial Fourier data, and inspect its output "
    "for any obvious artefacts. ";

  ARGUMENTS
  + Argument ("in", "the input image.").type_image_in ()
  + Argument ("out", "the output image.").type_image_out ();

  OPTIONS
  + Option ("axes",
            "select the slice axes (default: 0,1 - i.e. x-y).")
  +   Argument ("list").type_sequence_int ()

  + Option ("nshifts", "discretization of subpixel spacing (default: 20).")
  +   Argument ("value").type_integer (8, 128)

  + Option ("minW", "left border of window used for TV computation (default: 1).")
  +   Argument ("value").type_integer (0, 10)

  + Option ("maxW", "right border of window used for TV computation (default: 3).")
  +   Argument ("value").type_integer (0, 128)

  + DataType::options();

  REFERENCES
    + "Kellner, E; Dhital, B; Kiselev, V.G & Reisert, M. "
    "Gibbs-ringing artifact removal based on local subvoxel-shifts. "
    "Magnetic Resonance in Medicine, 2016, 76, 1574–1581.";

}

typedef double value_type;

class ComputeSlice
{ MEMALIGN (ComputeSlice)
  public:
    ComputeSlice (const vector<size_t>& outer_axes, const vector<size_t>& slice_axes, const int& nsh, const int& minW, const int& maxW, Image<value_type>& in, Image<value_type>& out) :
      outer_axes (outer_axes),
      slice_axes (slice_axes),
      nsh (nsh),
      minW (minW),
      maxW (maxW),
      in (in),
      out (out),
      im1 (in.size(slice_axes[0]), in.size(slice_axes[1])),
      im2 (im1.rows(), im1.cols()) {
        prealloc_FFT();
      }

    ComputeSlice (const ComputeSlice& other) :
      outer_axes (other.outer_axes),
      slice_axes (other.slice_axes),
      nsh (other.nsh),
      minW (other.minW),
      maxW (other.maxW),
      in (other.in),
      out (other.out),
      fft (),
      im1 (in.size(slice_axes[0]), in.size(slice_axes[1])),
      im2 (im1.rows(), im1.cols()) {
        prealloc_FFT();
      }

    void operator() (const Iterator& pos)
    {
      const int X = slice_axes[0];
      const int Y = slice_axes[1];
      assign_pos_of (pos, outer_axes).to (in, out);

      for (auto l = Loop (slice_axes) (in); l; ++l)
        im1 (in.index(X), in.index(Y)) = cdouble (in.value(), 0.0);

      unring_2d ();

      for (auto l = Loop (slice_axes) (out); l; ++l)
        out.value() = im1 (out.index(X), out.index(Y)).real();
    }

  private:
    const vector<size_t>& outer_axes;
    const vector<size_t>& slice_axes;
    const int nsh, minW, maxW;
    Image<value_type> in, out;
    Eigen::FFT<double> fft;
    Eigen::MatrixXcd im1, im2, shifted;
    Eigen::VectorXcd v;

    void prealloc_FFT () {
      // needed to avoid within-thread allocations,
      // which aren't thread-safe in FFTW:
#ifdef EIGEN_FFTW_DEFAULT
      Eigen::VectorXcd tmp (im1.rows());
      FFT (tmp);
      iFFT (tmp);
      tmp.resize (im1.cols());
      FFT (tmp);
      iFFT (tmp);
#endif
    }

    template <typename Derived> FORCE_INLINE void FFT      (Eigen::MatrixBase<Derived>&& vec) { fft.fwd (v, vec); vec = v; }
    template <typename Derived> FORCE_INLINE void FFT      (Eigen::MatrixBase<Derived>& vec) { FFT (std::move (vec)); }
    template <typename Derived> FORCE_INLINE void iFFT     (Eigen::MatrixBase<Derived>&& vec) { fft.inv (v, vec); vec = v; }
    template <typename Derived> FORCE_INLINE void iFFT     (Eigen::MatrixBase<Derived>& vec) { iFFT (std::move (vec)); }
    template <typename Derived> FORCE_INLINE void row_FFT  (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.rows(); ++n)  FFT (mat.row(n)); }
    template <typename Derived> FORCE_INLINE void row_iFFT (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.rows(); ++n) iFFT (mat.row(n)); }
    template <typename Derived> FORCE_INLINE void col_FFT  (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.cols(); ++n)  FFT (mat.col(n)); }
    template <typename Derived> FORCE_INLINE void col_iFFT (Eigen::MatrixBase<Derived>& mat) { for (auto n = 0; n < mat.cols(); ++n) iFFT (mat.col(n)); }

    FORCE_INLINE void unring_2d ()
    {
      row_FFT (im1);
      col_FFT (im1);

      for (int k = 0; k < im1.cols(); k++) {
        double ck = (1.0+cos(2.0*Math::pi*(double(k)/im1.cols())))*0.5;
        for (int j = 0 ; j < im1.rows(); j++) {
          double cj = (1.0+cos(2.0*Math::pi*(double(j)/im1.rows())))*0.5;

          if (ck+cj != 0.0) {
            im2(j,k) = im1(j,k) * cj / (ck+cj);
            im1(j,k) *= ck / (ck+cj);
          }
          else
            im1(j,k) = im2(j,k) = cdouble(0.0, 0.0);
        }
      }

      row_iFFT (im1);
      col_iFFT (im2);

      unring_1d (im1);
      unring_1d (im2.transpose());

      im1 += im2;
    }

    template <typename Derived>
      FORCE_INLINE void unring_1d (Eigen::MatrixBase<Derived>&& eig)
      {
        const int n = eig.rows();
        const int numlines = eig.cols();
        shifted.resize (n, 2*nsh+1);

        int shifts [2*nsh+1];
        shifts[0] = 0;
        for (int j = 0; j < nsh; j++) {
          shifts[j+1] = j+1;
          shifts[1+nsh+j] = -(j+1);
        }

        double TV1arr[2*nsh+1];
        double TV2arr[2*nsh+1];

        for (int k = 0; k < numlines; k++) {
          shifted.col(0) = eig.col(k);

          const int maxn = (n&1) ? (n-1)/2 : n/2-1;

          for (int j = 1; j < 2*nsh+1; j++) {
            double phi = Math::pi*double(shifts[j])/double(n*nsh);
            cdouble u (std::cos(phi), std::sin(phi));
            cdouble e (1.0, 0.0);
            shifted(0,j) = shifted(0,0);

            if (!(n&1))
              shifted(n/2,j) = cdouble(0.0, 0.0);

            for (int l = 0; l < maxn; l++) {
              e = u*e;
              int L = l+1; shifted(L,j) = e * shifted(L,0);
              L = n-1-l;   shifted(L,j) = std::conj(e) * shifted(L,0);
            }
          }

          col_iFFT (shifted);

          for (int j = 0; j < 2*nsh+1; ++j) {
            TV1arr[j] = 0.0;
            TV2arr[j] = 0.0;
            for (int t = minW; t <= maxW; t++) {
              TV1arr[j] += std::abs (shifted((n-t)%n,j).real() - shifted((n-t-1)%n,j).real());
              TV1arr[j] += std::abs (shifted((n-t)%n,j).imag() - shifted((n-t-1)%n,j).imag());
              TV2arr[j] += std::abs (shifted((n+t)%n,j).real() - shifted((n+t+1)%n,j).real());
              TV2arr[j] += std::abs (shifted((n+t)%n,j).imag() - shifted((n+t+1)%n,j).imag());
            }
          }

          for (int l = 0; l < n; ++l) {
            double minTV = std::numeric_limits<double>::max();
            int minidx = 0;
            for (int j = 0; j < 2*nsh+1; ++j) {

              if (TV1arr[j] < minTV) {
                minTV = TV1arr[j];
                minidx = j;
              }
              if (TV2arr[j] < minTV) {
                minTV = TV2arr[j];
                minidx = j;
              }

              TV1arr[j] += std::abs (shifted((l-minW+1+n)%n,j).real() - shifted((l-(minW  )+n)%n,j).real());
              TV1arr[j] -= std::abs (shifted((l-maxW  +n)%n,j).real() - shifted((l-(maxW+1)+n)%n,j).real());
              TV2arr[j] += std::abs (shifted((l+maxW+1+n)%n,j).real() - shifted((l+(maxW+2)+n)%n,j).real());
              TV2arr[j] -= std::abs (shifted((l+minW  +n)%n,j).real() - shifted((l+(minW+1)+n)%n,j).real());

              TV1arr[j] += std::abs (shifted((l-minW+1+n)%n,j).imag() - shifted((l-(minW  )+n)%n,j).imag());
              TV1arr[j] -= std::abs (shifted((l-maxW  +n)%n,j).imag() - shifted((l-(maxW+1)+n)%n,j).imag());
              TV2arr[j] += std::abs (shifted((l+maxW+1+n)%n,j).imag() - shifted((l+(maxW+2)+n)%n,j).imag());
              TV2arr[j] -= std::abs (shifted((l+minW  +n)%n,j).imag() - shifted((l+(minW+1)+n)%n,j).imag());
            }

            double a0r = shifted((l-1+n)%n,minidx).real();
            double a1r = shifted(l,minidx).real();
            double a2r = shifted((l+1+n)%n,minidx).real();
            double a0i = shifted((l-1+n)%n,minidx).imag();
            double a1i = shifted(l,minidx).imag();
            double a2i = shifted((l+1+n)%n,minidx).imag();
            double s = double(shifts[minidx])/(2.0*nsh);

            if (s > 0.0)
              eig(l,k) = cdouble (a1r*(1.0-s) + a0r*s, a1i*(1.0-s) + a0i*s);
            else
              eig(l,k) = cdouble (a1r*(1.0+s) - a2r*s, a1i*(1.0+s) - a2i*s);
          }
        }
      }

    template <typename Derived>
      FORCE_INLINE void unring_1d (Eigen::MatrixBase<Derived>& eig) { unring_1d (std::move (eig)); }

};

void run ()
{
  const int nshifts = App::get_option_value ("nshifts", 20);
  const int minW = App::get_option_value ("minW", 1);
  const int maxW = App::get_option_value ("maxW", 3);

  if (minW >= maxW)
    throw Exception ("minW must be smaller than maxW");

  auto in = Image<value_type>::open (argument[0]);
  Header header (in);

  header.datatype() = DataType::from_command_line (DataType::Float32);
  auto out = Image<value_type>::create (argument[1], header);

  vector<size_t> slice_axes = { 0, 1 };
  auto opt = get_options ("axes");
  if (opt.size()) {
    vector<int> axes = opt[0][0];
    if (slice_axes.size() != 2)
      throw Exception ("slice axes must be specified as a comma-separated 2-vector");
    slice_axes = { size_t(axes[0]), size_t(axes[1]) };
  }

  // build vector of outer axes:
  vector<size_t> outer_axes (header.ndim());
  std::iota (outer_axes.begin(), outer_axes.end(), 0);
  for (const auto axis : slice_axes) {
    auto it = std::find (outer_axes.begin(), outer_axes.end(), axis);
    if (it == outer_axes.end())
      throw Exception ("slice axis out of range!");
    outer_axes.erase (it);
  }

  ThreadedLoop ("performing Gibbs ringing removal", in, outer_axes, slice_axes)
    .run_outer (ComputeSlice (outer_axes, slice_axes, nshifts, minW, maxW, in, out));
}

<|endoftext|>


<commit_before>#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;

#define ll long long
#define ull unsigned long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr))

const int maxN=1010;
const int maxL=110;
const ull base=13;
const int inf=2147483647;

int n,m,L;
char str[maxN][maxL];
ull Hash[maxN],Base[maxN];
int Ans[maxN];
multiset<ull> Ms;

int main(){
	Base[0]=1;for (int i=1;i<maxN;i++) Base[i]=Base[i-1]*base;
	scanf("%d%d%d",&n,&L,&m);
	for (int i=1;i<=n;i++) scanf("%s",str[i]+1);
	for (int i=1;i<=n;i++){
		for (int j=1;j<=L;j++) Hash[i]=Hash[i]*base+(ull)(str[i][j]-'a');
		Ms.insert(Hash[i]);
	}
	for (int i=1;i<=n;i++) Ans[i]=Ms.count(Hash[i]);
	//cout<<"Input over"<<endl;
	//for (int i=1;i<=n;i++) cout<<Hash[i]<<" ";cout<<endl;
	while (m--){
		int p1,w1,p2,w2;scanf("%d%d%d%d",&p1,&w1,&p2,&w2);
		Ms.erase(Ms.find(Hash[p1]));if (p1!=p2) Ms.erase(Ms.find(Hash[p2]));
		Hash[p1]-=(ull)(str[p1][w1]-'a')*Base[L-w1];
		Hash[p2]-=(ull)(str[p2][w2]-'a')*Base[L-w2];
		swap(str[p1][w1],str[p2][w2]);
		Hash[p1]+=(ull)(str[p1][w1]-'a')*Base[L-w1];
		Hash[p2]+=(ull)(str[p2][w2]-'a')*Base[L-w2];
		Ms.insert(Hash[p1]);if (p1!=p2) Ms.insert(Hash[p2]);
		for (int i=1;i<=n;i++) Ans[i]=max(Ans[i],(int)Ms.count(Hash[i]));
		//for (int i=1;i<=n;i++) cout<<Hash[i]<<" ";cout<<endl;
	}

	for (int i=1;i<=n;i++) printf("%d\n",Ans[i]);

	return 0;
}
<commit_msg>2018.9.8 noon<commit_after>#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;

#define ll long long
#define ull unsigned long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr))

const int maxN=1510;
const int maxL=120;
const ull base=17;
const int inf=2147483647;

class Heap
{
public:
	int fa,ls,rs,size,rdis;
	int Ans,mx;
};

int n,L,m;
char str[maxN][maxL];
ull Hash[maxN],Pow[maxN];
int rcnt,St[maxN];
Heap H[maxN];
map<ull,int> Rt;

void PushDown(int u);
void Update(int u);
int Merge(int u,int v);
void Erase(int x);
void Insert(int x);

int main(){
	Pow[0]=1;for (int i=1;i<maxN;i++) Pow[i]=Pow[i-1]*base;
	scanf("%d%d%d",&n,&L,&m);rcnt=n;
	for (int i=1;i<=n;i++) scanf("%s",str[i]+1);
	for (int i=1;i<=n;i++){
		Hash[i]=0;H[i].size=H[i].Ans=1;
		for (int j=1;j<=L;j++) Hash[i]=Hash[i]*base+str[i][j]-'a';
		Insert(i);
	}

	while (m--){
		int p1,p2,w1,w2;
		scanf("%d%d%d%d",&p1,&w1,&p2,&w2);
		Erase(p1);if (p1!=p2) Erase(p2);

		Hash[p1]-=Pow[L-w1]*(str[p1][w1]-'a');
		Hash[p2]-=Pow[L-w2]*(str[p2][w2]-'a');
		swap(str[p1][w1],str[p2][w2]);
		Hash[p1]+=Pow[L-w1]*(str[p1][w1]-'a');
		Hash[p2]+=Pow[L-w2]*(str[p2][w2]-'a');

		Insert(p1);if (p1!=p2) Insert(p2);

		//for (int i=1;i<=n;i++) cout<<Hash[i]<<" ";cout<<endl;
		//cout<<"fa ls rs size ans mx"<<endl;
		//for (int i=1;i<=n;i++) cout<<" "<<H[i].fa<<"  "<<H[i].ls<<"  "<<H[i].rs<<"    "<<H[i].size<<"   "<<H[i].Ans<<"  "<<H[i].mx<<endl;
	}

	for (int i=1;i<=n;i++) Erase(i);

	for (int i=1;i<=n;i++) printf("%d\n",H[i].Ans);
	return 0;
}

void PushDown(int u){
	if (H[u].ls){
		int v=H[u].ls;
		H[v].Ans=max(H[v].Ans,H[u].mx);
		H[v].mx=max(H[v].mx,H[u].mx);
	}
	if (H[u].rs){
		int v=H[u].rs;
		H[v].Ans=max(H[v].Ans,H[u].mx);
		H[v].mx=max(H[v].mx,H[u].mx);
	}
	H[u].mx=0;
	return;
}

void Update(int u){
	H[u].size=H[H[u].ls].size+H[H[u].rs].size+1;
	return;
}

int Merge(int u,int v){
	if (u==0) return v;
	if (v==0) return u;
	PushDown(u);PushDown(v);
	int rt=H[u].rs=Merge(H[u].rs,v);if (rt!=0) H[rt].fa=u;Update(u);
	if (H[H[u].ls].rdis<H[H[u].rs].rdis) swap(H[u].ls,H[u].rs);
	H[u].rdis=H[H[u].rs].rdis+1;
	return u;
}

void Erase(int x){
	int now=x,top=0;
	while (now) St[++top]=now,now=H[now].fa;
	while (top) PushDown(St[top--]);
	if (Rt[Hash[x]]==x){
		int ls=H[x].ls,rs=H[x].rs;
		H[ls].fa=H[rs].fa=H[x].ls=H[x].rs=0;
		Rt[Hash[x]]=Merge(ls,rs);Update(x);
		if (Rt[Hash[x]]==0) Rt.erase(Hash[x]);
	}
	else{
		int ls=H[x].ls,rs=H[x].rs,fa=H[x].fa;H[ls].fa=H[rs].fa=H[x].ls=H[x].rs=0;
		int r=Merge(ls,rs);if (r!=0) H[r].fa=fa;
		if (H[fa].ls==x) H[fa].ls=r;
		else H[fa].rs=r;
		int now=fa;
		while (now) Update(now),now=H[now].fa;
		Update(x);
	}
	return;
}

void Insert(int x){
	if (Rt.count(Hash[x])==0) Rt[Hash[x]]=x;
	else Rt[Hash[x]]=Merge(Rt[Hash[x]],x);
	H[Rt[Hash[x]]].mx=max(H[Rt[Hash[x]]].mx,H[Rt[Hash[x]]].size);
	H[Rt[Hash[x]]].Ans=max(H[Rt[Hash[x]]].Ans,H[Rt[Hash[x]]].mx);
	return;
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_BOUNDARY_CONDITION
#define MJOLNIR_BOUNDARY_CONDITION
#include <cassert>

namespace mjolnir
{

template<typename traitsT>
struct UnlimitedBoundary
{
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

    UnlimitedBoundary() = default;
    ~UnlimitedBoundary() = default;

    static coordinate_type
    adjust_direction(coordinate_type dr) {return dr;}
    static coordinate_type
    adjust_absolute(coordinate_type r) {return r;}
};

template<typename traitsT>
struct PeriodicBoundaryXYZ
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

  public:
    PeriodicBoundaryXYZ() = default;
    ~PeriodicBoundaryXYZ() = default;

    static void
    set_system(const coordinate_type& lower, const coordinate_type upper)
    {
        lower_            = lower;
        upper_            = upper;
        sys_size_         = upper_ - lower_;
        sys_size_half_pos =  0.5 * sys_size_;
        sys_size_half_neg = -0.5 * sys_size_;
        return;
    }

    static coordinate_type
    adjust_direction(coordinate_type dr)
    {
             if(dr[0] < sys_size_half_neg[0]) dr[0] += sys_size_[0];
        else if(dr[0] > sys_size_half_pos[0]) dr[0] -= sys_size_[0];
             if(dr[1] < sys_size_half_neg[1]) dr[1] += sys_size_[1];
        else if(dr[1] > sys_size_half_pos[1]) dr[1] -= sys_size_[1];
             if(dr[2] < sys_size_half_neg[2]) dr[2] += sys_size_[2];
        else if(dr[2] > sys_size_half_pos[2]) dr[2] -= sys_size_[2];
        return dr;
    }

    coordinate_type
    adjust_absolute(coordinate_type pos)
    {
             if(pos[0] < lower_[0]) pos[0] += sys_size_[0];
        else if(pos[0] > upper_[0]) pos[0] -= sys_size_[0];
             if(pos[1] < lower_[1]) pos[1] += sys_size_[1];
        else if(pos[1] > upper_[1]) pos[1] -= sys_size_[1];
             if(pos[2] < lower_[2]) pos[2] += sys_size_[2];
        else if(pos[2] > upper_[2]) pos[2] -= sys_size_[2];
        return pos;
    }

  private:

    static coordinate_type lower_;
    static coordinate_type upper_;
    static coordinate_type sys_size_;
    static coordinate_type sys_size_half_pos;
    static coordinate_type sys_size_half_neg;
};

template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::lower_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::upper_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_half_pos;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_half_neg;

}//mjolnir
#endif /* MJOLNIR_BOUNDARY_CONDITION */
<commit_msg>add missing static specifier<commit_after>#ifndef MJOLNIR_BOUNDARY_CONDITION
#define MJOLNIR_BOUNDARY_CONDITION
#include <cassert>

namespace mjolnir
{

template<typename traitsT>
struct UnlimitedBoundary
{
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

    UnlimitedBoundary() = default;
    ~UnlimitedBoundary() = default;

    static coordinate_type
    adjust_direction(coordinate_type dr) {return dr;}
    static coordinate_type
    adjust_absolute(coordinate_type r) {return r;}
};

template<typename traitsT>
struct PeriodicBoundaryXYZ
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

  public:
    PeriodicBoundaryXYZ() = default;
    ~PeriodicBoundaryXYZ() = default;

    static void
    set_system(const coordinate_type& lower, const coordinate_type upper)
    {
        lower_            = lower;
        upper_            = upper;
        sys_size_         = upper_ - lower_;
        sys_size_half_pos =  0.5 * sys_size_;
        sys_size_half_neg = -0.5 * sys_size_;
        return;
    }

    static coordinate_type
    adjust_direction(coordinate_type dr)
    {
             if(dr[0] < sys_size_half_neg[0]) dr[0] += sys_size_[0];
        else if(dr[0] > sys_size_half_pos[0]) dr[0] -= sys_size_[0];
             if(dr[1] < sys_size_half_neg[1]) dr[1] += sys_size_[1];
        else if(dr[1] > sys_size_half_pos[1]) dr[1] -= sys_size_[1];
             if(dr[2] < sys_size_half_neg[2]) dr[2] += sys_size_[2];
        else if(dr[2] > sys_size_half_pos[2]) dr[2] -= sys_size_[2];
        return dr;
    }

    static coordinate_type
    adjust_absolute(coordinate_type pos)
    {
             if(pos[0] < lower_[0]) pos[0] += sys_size_[0];
        else if(pos[0] > upper_[0]) pos[0] -= sys_size_[0];
             if(pos[1] < lower_[1]) pos[1] += sys_size_[1];
        else if(pos[1] > upper_[1]) pos[1] -= sys_size_[1];
             if(pos[2] < lower_[2]) pos[2] += sys_size_[2];
        else if(pos[2] > upper_[2]) pos[2] -= sys_size_[2];
        return pos;
    }

  private:

    static coordinate_type lower_;
    static coordinate_type upper_;
    static coordinate_type sys_size_;
    static coordinate_type sys_size_half_pos;
    static coordinate_type sys_size_half_neg;
};

template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::lower_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::upper_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_half_pos;
template<typename traitsT>
typename PeriodicBoundaryXYZ<traitsT>::coordinate_type
PeriodicBoundaryXYZ<traitsT>::sys_size_half_neg;

}//mjolnir
#endif /* MJOLNIR_BOUNDARY_CONDITION */
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//  cmd_zones.cc : The "zones" command
//
//////////////////////////////////////////////////////////////////////////

#include <algorithm>

#include "stdsneezy.h"
#include "database.h"
#include "cmd_trophy.h"

class zoneSorter {
  public:
    float avgLevel;
    sstring zoneName;
    zoneSorter(float n, const char *s) :
      avgLevel(n), zoneName(s)
    {}
    zoneSorter() : avgLevel(0.0), zoneName("") {}
    bool operator() (const zoneSorter &, const zoneSorter &) const;
};

bool zoneSorter::operator()  (const zoneSorter &x, const zoneSorter &y) const
{
  return x.avgLevel < y.avgLevel;
}

void TBeing::doZonesSingle(sstring tStString)
{
  unsigned int iZone        = 0/*,
	       iZoneIter    = 0*/;
  char         tString[256] = "\0",
               tBuffer[256] = "\0",
               tStats[2048] = "\0";
  sstring      tStBuffer(""),
               tStTemp("");

  for (iZone = 0; iZone < zone_table.size(); iZone++) {
    // skip the void (0) : this is for misc. mobs
    // skip immort zone (1)
    if (iZone == 0 || iZone == 1)
      continue;

    zoneData & zd      = zone_table[iZone];
    int        iBottom = (iZone ? (zone_table[(iZone - 1)].top + 1) : 0);

    // If the string is not in this zone, just continue.
    if (!strstr(zd.name, tStString.c_str()))
      continue;

    // Now trim the zonename so it matches what is shown in 'zones'.
    char buf[256];

    strcpy(buf, zd.name);
    char *s = strchr(buf, '-');
    char *n = buf;

    if (s) {
      --s;       // get the space before the -
      *s = '\0';
      s += 2;    // get the space after the -
      *s = '\0'; // after zone name
      ++s;
    } else {
      s = buf;
      n = NULL;
    }

    if (!s || !zd.enabled || !zd.num_mobs || !is_abbrev(tStString, s))
      continue;

    strcpy(tString, zd.name);

    float avg = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);
    sprintf(tBuffer, "%-30.30s : Level: avg: %3.0f, min: %3.0f, max %3.0f\n\r", tString, avg, zd.min_mob_level, zd.max_mob_level);

    if (isPc() && trophy) {
      // { -- Deduce zone size --
/* removed at Damescena's request
        int iRCount = ((zd.top - zd.bottom) + 1);
        strcpy(tStats, "This zone is ");

        if (iRCount > 500)
          strcat(tStats, "Huge!");
        else if (iRCount > 200)
          strcat(tStats, "Large!");
        else if (iRCount > 100)
          strcat(tStats, "a good size.");
        else if (iRCount > 50)
          strcat(tStats, "medium sized.");
        else
          strcat(tStats, "small, but has a down home feeling...");

        strcat(tStats, "\n\r");
*/
      // }

      // { -- Deduce mobiles fought in this zone, by this player, for both distict fight count and average xp gain --
        unsigned int iCount  = 0,
                     iFought = 0;
        float        fXPAvg  = 0.0; // 1.00 = 100%
        TTrophy    & tTrophy = *trophy;
        TDatabase    db(DB_SNEEZY);

        db.query("select mobvnum, count from trophy where player_id=%i order by mobvnum", getPlayerID());

        while (1) {
          if (!db.fetchRow())
            break;

          int iVNum = convertTo<int>(db["mobvnum"]);

          if (!iVNum)
            continue;

          if (iVNum > zd.top)
            break;

          int iRNum = real_mobile(convertTo<int>(db["mobvnum"]));

          if (iRNum < 0) {
            vlogf(LOG_BUG, fmt("doZones detected bad mobvnum=%d for name='%s'") % iVNum % getName());
            continue;
          }

          if (!mob_index[iRNum].doesLoad)
            continue;

          if (convertTo<float>(db["count"]))
            for (unsigned int iRunner = 0; iRunner < mob_index.size(); iRunner++)
              if ((mob_index[iRunner].virt >= iBottom) && (mob_index[iRunner].virt <= zd.top) && mob_index[iRunner].doesLoad) {
                iCount++;

                fXPAvg += tTrophy.getExpModVal(tTrophy.getCount(mob_index[iRunner].virt), mob_index[iRunner].virt);

                if (tTrophy.getCount(mob_index[iRunner].virt) > 0)
                  iFought++;
              }

          break;
        }

        if (iCount) { // If there are no mobs here, don't bother with the below.
          double fFPer = ((iFought * 100) / iCount);

          if (fXPAvg)
            fXPAvg /= iCount;

          sprintf(&tStats[strlen(tStats)], "You have fought %u distict creatures in this zone\n\r", iFought);
          sprintf(&tStats[strlen(tStats)], "...which equates to %.0f%% of all the creatures found within it.\n\r", fFPer);
          strcat(tStats, "All in all you will get ");

          if (fXPAvg >= 1.0)
            strcat(tStats, "full");
          else if (fXPAvg >= .90)
            strcat(tStats, "most of the");
          else if (fXPAvg >= .70)
            strcat(tStats, "a lot of the");
          else if (fXPAvg >= .50)
            strcat(tStats, "a decent amount of the");
          else if (fXPAvg >= .20)
            strcat(tStats, "some of the");
          else if (fXPAvg >= .10)
            strcat(tStats, "a tiny amount of the");
          else if (fXPAvg)
            strcat(tStats, "a little of the");
          else
            strcat(tStats, "no");

          strcat(tStats, " experience from creatures in this zone.\n\r");
        }
      // }
    }

    break;
  }

  if (!tBuffer[0]) {
    unsigned int iZIndex = 0,
                 zone    = 0;

    // Might be a number, check it and find out.
    iZIndex = atoi(tStString.c_str());

    // Is a digit, find the appropriate zone here.
    vector <zoneSorter> sortZoneVec(0);

    for (zone = 0; zone < zone_table.size(); zone++) {
      // skip the void (0) : this is for misc. mobs
      // skip immort zone (1)
      if (zone == 0 || zone == 1)
	continue;

      zoneData &zd = zone_table[zone];

      if (!zd.enabled || !zd.num_mobs)
	continue;

      char buf[256];
      strcpy(buf, zd.name);

      char *s = strchr(buf, '-');
      char *n = buf;

      if (s) {
	--s;       // get the space before the -
	*s = '\0'; // after builder name
	s += 2;    // get the space after the -
	*s = '\0'; // after zone name
	++s;
      } else {
	s = buf;
	n = NULL;
      }

      // buf is now the builder name, s is the zone name
      float avg = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);

      double x, total = 0, dev, minlev = 1000, maxlev = -1;
      int virt, count = 0;

      for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
	virt = mob_index[mobnum].virt;

	if(virt > zone_table[zone-1].top && virt <= zone_table[zone].top && mob_index[mobnum].doesLoad) {
	  count += mob_index[mobnum].getMaxNumber();
	  x=(mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());

	  if(mob_index[mobnum].level>maxlev)
	    maxlev = mob_index[mobnum].level;

	  if(mob_index[mobnum].level<minlev)
	    minlev = mob_index[mobnum].level;

	  x -= avg;
	  x *= x;
	  total += x;
	}
      }
      total /= (count-1);
      dev = sqrt(total);

      total = count = 0;

      // now go through the mobs again and average up only a few
      for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
	virt = mob_index[mobnum].virt;
	if (virt > zone_table[zone-1].top && virt <=  zone_table[zone].top && mob_index[mobnum].doesLoad) {
	  if(mob_index[mobnum].level < avg+dev && mob_index[mobnum].level > avg-dev) {
	    total+= (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());
	    count+= mob_index[mobnum].getMaxNumber();
	  }
	}
      }

      if (count > 0 && total > 0)
	avg = total / count;

      sortZoneVec.push_back(zoneSorter(avg, s));
    }

    // sort the vector
    sort(sortZoneVec.begin(), sortZoneVec.end(), zoneSorter());

    if ((iZIndex == 0) || (iZIndex >= sortZoneVec.size())) {
      iZIndex = (sortZoneVec.size() - 1);

      sendTo(fmt("Requested zone index is out of range.  Range is: 1, ..., %u\n\r") % iZIndex);
    } else
      doZonesSingle(sortZoneVec[(iZIndex - 1)].zoneName);

    return;
  }

  tStBuffer += tBuffer;

  if (iZone >= 0 && iZone < zone_table.size()) {
    sprintf(tString, "zoneHelp/%d", (iZone > 0 ? (zone_table[iZone - 1].top + 1) : 0));

    if (file_to_sstring(tString, tStTemp)) {
      tStBuffer += "\n\r";
      tStBuffer += tStTemp;
    }
  }

  if (strlen(tStats)) {
    tStBuffer += "\n\r";
    tStBuffer += tStats;
  }

  desc->page_string(tStBuffer);
}

void TBeing::doZones(sstring tStString)
{
  if (!tStString.empty()) {
    doZonesSingle(tStString);
    return;
  }

  unsigned int zone;
  sstring      str;
  int          cIndex = 0;

  const char *colorStrings[] = { "<r>", "<g>", "<p>", "<k>", "<b>", "<c>", "<o>", "<z>" };

  if (!desc)
    return;

  str = "The level information presented here is based on statistical averaging of the\n\r";
  str += "creatures found therein, and may not accurately reflect the intended level\n\r";
  str += "for players.  The levels shown may also fluxuate from moment to moment.\n\r";
  str += "\n\r";

  vector<zoneSorter>sortZoneVec(0);

  for (zone = 0; zone < zone_table.size(); zone++) {
    // skip the void (0) : this is for misc. mobs
    // skip immort zone (1)
    if (zone == 0 || zone == 1)
      continue;

    zoneData & zd = zone_table[zone];

    if (!zd.enabled)
      continue;

    if (!zd.num_mobs)
      continue;

    char buf[256],
         buf2[256];

    strcpy(buf, zd.name);
    char *s = strchr(buf, '-');
    char *n = buf;

    if (s) {
      --s;       // get the space before the -
      *s = '\0'; // after builder name
      s += 2;    // get the space after the -
      *s = '\0'; // after zone name
      ++s;
    } else {
      s = buf;
      n = NULL;
    }

    // buf is now the builder name, s is the zone name
    float  avg    = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);
    double x,
           total  = 0,
           dev,
           minlev = 1000,
           maxlev = -1;
    int    virt,
           count  = 0;

    for(unsigned int mobnum=0;mobnum<mob_index.size();mobnum++){
      virt=mob_index[mobnum].virt;

      if (virt > zone_table[zone-1].top && virt <=  zone_table[zone].top && mob_index[mobnum].doesLoad) {
	count += mob_index[mobnum].getMaxNumber();
	x      = (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());

	if (mob_index[mobnum].level>maxlev)
	  maxlev = mob_index[mobnum].level;

	if (mob_index[mobnum].level<minlev)
	  minlev = mob_index[mobnum].level;

	x     -= avg;
	x     *= x;
	total += x;

      }
    }

    total /= (count-1);
    dev    = sqrt(total);
    total  = count = 0;

    // now go through the mobs again and average up only a few
    for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
      virt = mob_index[mobnum].virt;

      if (virt > zone_table[zone-1].top && virt <= zone_table[zone].top && mob_index[mobnum].doesLoad) {
	if (mob_index[mobnum].level < avg+dev && mob_index[mobnum].level > avg-dev) {
	  total += (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());
	  count += mob_index[mobnum].getMaxNumber();
	}
      }
    }

    if(count > 0 && total > 0)
      avg = total / count;

    if(minlev == 1000)
      minlev = zd.min_mob_level;

    if(maxlev == -1)
      maxlev = zd.max_mob_level;

    sprintf(buf2, "%-25.25s : %-10.10s : Level: avg: %i, min: %3.0f, max %3.0f\n\r", s, (n ? n : ""), (int)avg, minlev, maxlev);

    sortZoneVec.push_back(zoneSorter(avg, buf2));
  }

  // sort the vector
  sort(sortZoneVec.begin(), sortZoneVec.end(), zoneSorter());

  // list is sorted by avg level, cat it all together now
  int lastavg = -1;

  for (zone = 0; zone < sortZoneVec.size(); zone++) {
    char tString[256];

    if ((int)(sortZoneVec[zone].avgLevel) != lastavg) {
      if (++cIndex == 8)
	cIndex = 0;

      lastavg = (int)(sortZoneVec[zone].avgLevel);
    }

    sprintf(tString, "%d. %s%s<z>", (zone + 1), colorStrings[cIndex], sortZoneVec[zone].zoneName.c_str());
    str += tString;
  }

  desc->page_string(str);
}
<commit_msg>fixed spelling error<commit_after>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//  cmd_zones.cc : The "zones" command
//
//////////////////////////////////////////////////////////////////////////

#include <algorithm>

#include "stdsneezy.h"
#include "database.h"
#include "cmd_trophy.h"

class zoneSorter {
  public:
    float avgLevel;
    sstring zoneName;
    zoneSorter(float n, const char *s) :
      avgLevel(n), zoneName(s)
    {}
    zoneSorter() : avgLevel(0.0), zoneName("") {}
    bool operator() (const zoneSorter &, const zoneSorter &) const;
};

bool zoneSorter::operator()  (const zoneSorter &x, const zoneSorter &y) const
{
  return x.avgLevel < y.avgLevel;
}

void TBeing::doZonesSingle(sstring tStString)
{
  unsigned int iZone        = 0/*,
	       iZoneIter    = 0*/;
  char         tString[256] = "\0",
               tBuffer[256] = "\0",
               tStats[2048] = "\0";
  sstring      tStBuffer(""),
               tStTemp("");

  for (iZone = 0; iZone < zone_table.size(); iZone++) {
    // skip the void (0) : this is for misc. mobs
    // skip immort zone (1)
    if (iZone == 0 || iZone == 1)
      continue;

    zoneData & zd      = zone_table[iZone];
    int        iBottom = (iZone ? (zone_table[(iZone - 1)].top + 1) : 0);

    // If the string is not in this zone, just continue.
    if (!strstr(zd.name, tStString.c_str()))
      continue;

    // Now trim the zonename so it matches what is shown in 'zones'.
    char buf[256];

    strcpy(buf, zd.name);
    char *s = strchr(buf, '-');
    char *n = buf;

    if (s) {
      --s;       // get the space before the -
      *s = '\0';
      s += 2;    // get the space after the -
      *s = '\0'; // after zone name
      ++s;
    } else {
      s = buf;
      n = NULL;
    }

    if (!s || !zd.enabled || !zd.num_mobs || !is_abbrev(tStString, s))
      continue;

    strcpy(tString, zd.name);

    float avg = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);
    sprintf(tBuffer, "%-30.30s : Level: avg: %3.0f, min: %3.0f, max %3.0f\n\r", tString, avg, zd.min_mob_level, zd.max_mob_level);

    if (isPc() && trophy) {
      // { -- Deduce zone size --
/* removed at Damescena's request
        int iRCount = ((zd.top - zd.bottom) + 1);
        strcpy(tStats, "This zone is ");

        if (iRCount > 500)
          strcat(tStats, "Huge!");
        else if (iRCount > 200)
          strcat(tStats, "Large!");
        else if (iRCount > 100)
          strcat(tStats, "a good size.");
        else if (iRCount > 50)
          strcat(tStats, "medium sized.");
        else
          strcat(tStats, "small, but has a down home feeling...");

        strcat(tStats, "\n\r");
*/
      // }

      // { -- Deduce mobiles fought in this zone, by this player, for both distinct fight count and average xp gain --
        unsigned int iCount  = 0,
                     iFought = 0;
        float        fXPAvg  = 0.0; // 1.00 = 100%
        TTrophy    & tTrophy = *trophy;
        TDatabase    db(DB_SNEEZY);

        db.query("select mobvnum, count from trophy where player_id=%i order by mobvnum", getPlayerID());

        while (1) {
          if (!db.fetchRow())
            break;

          int iVNum = convertTo<int>(db["mobvnum"]);

          if (!iVNum)
            continue;

          if (iVNum > zd.top)
            break;

          int iRNum = real_mobile(convertTo<int>(db["mobvnum"]));

          if (iRNum < 0) {
            vlogf(LOG_BUG, fmt("doZones detected bad mobvnum=%d for name='%s'") % iVNum % getName());
            continue;
          }

          if (!mob_index[iRNum].doesLoad)
            continue;

          if (convertTo<float>(db["count"]))
            for (unsigned int iRunner = 0; iRunner < mob_index.size(); iRunner++)
              if ((mob_index[iRunner].virt >= iBottom) && (mob_index[iRunner].virt <= zd.top) && mob_index[iRunner].doesLoad) {
                iCount++;

                fXPAvg += tTrophy.getExpModVal(tTrophy.getCount(mob_index[iRunner].virt), mob_index[iRunner].virt);

                if (tTrophy.getCount(mob_index[iRunner].virt) > 0)
                  iFought++;
              }

          break;
        }

        if (iCount) { // If there are no mobs here, don't bother with the below.
          double fFPer = ((iFought * 100) / iCount);

          if (fXPAvg)
            fXPAvg /= iCount;

          sprintf(&tStats[strlen(tStats)], "You have fought %u distinct creatures in this zone\n\r", iFought);
          sprintf(&tStats[strlen(tStats)], "...which equates to %.0f%% of all the creatures found within it.\n\r", fFPer);
          strcat(tStats, "All in all you will get ");

          if (fXPAvg >= 1.0)
            strcat(tStats, "full");
          else if (fXPAvg >= .90)
            strcat(tStats, "most of the");
          else if (fXPAvg >= .70)
            strcat(tStats, "a lot of the");
          else if (fXPAvg >= .50)
            strcat(tStats, "a decent amount of the");
          else if (fXPAvg >= .20)
            strcat(tStats, "some of the");
          else if (fXPAvg >= .10)
            strcat(tStats, "a tiny amount of the");
          else if (fXPAvg)
            strcat(tStats, "a little of the");
          else
            strcat(tStats, "no");

          strcat(tStats, " experience from creatures in this zone.\n\r");
        }
      // }
    }

    break;
  }

  if (!tBuffer[0]) {
    unsigned int iZIndex = 0,
                 zone    = 0;

    // Might be a number, check it and find out.
    iZIndex = atoi(tStString.c_str());

    // Is a digit, find the appropriate zone here.
    vector <zoneSorter> sortZoneVec(0);

    for (zone = 0; zone < zone_table.size(); zone++) {
      // skip the void (0) : this is for misc. mobs
      // skip immort zone (1)
      if (zone == 0 || zone == 1)
	continue;

      zoneData &zd = zone_table[zone];

      if (!zd.enabled || !zd.num_mobs)
	continue;

      char buf[256];
      strcpy(buf, zd.name);

      char *s = strchr(buf, '-');
      char *n = buf;

      if (s) {
	--s;       // get the space before the -
	*s = '\0'; // after builder name
	s += 2;    // get the space after the -
	*s = '\0'; // after zone name
	++s;
      } else {
	s = buf;
	n = NULL;
      }

      // buf is now the builder name, s is the zone name
      float avg = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);

      double x, total = 0, dev, minlev = 1000, maxlev = -1;
      int virt, count = 0;

      for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
	virt = mob_index[mobnum].virt;

	if(virt > zone_table[zone-1].top && virt <= zone_table[zone].top && mob_index[mobnum].doesLoad) {
	  count += mob_index[mobnum].getMaxNumber();
	  x=(mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());

	  if(mob_index[mobnum].level>maxlev)
	    maxlev = mob_index[mobnum].level;

	  if(mob_index[mobnum].level<minlev)
	    minlev = mob_index[mobnum].level;

	  x -= avg;
	  x *= x;
	  total += x;
	}
      }
      total /= (count-1);
      dev = sqrt(total);

      total = count = 0;

      // now go through the mobs again and average up only a few
      for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
	virt = mob_index[mobnum].virt;
	if (virt > zone_table[zone-1].top && virt <=  zone_table[zone].top && mob_index[mobnum].doesLoad) {
	  if(mob_index[mobnum].level < avg+dev && mob_index[mobnum].level > avg-dev) {
	    total+= (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());
	    count+= mob_index[mobnum].getMaxNumber();
	  }
	}
      }

      if (count > 0 && total > 0)
	avg = total / count;

      sortZoneVec.push_back(zoneSorter(avg, s));
    }

    // sort the vector
    sort(sortZoneVec.begin(), sortZoneVec.end(), zoneSorter());

    if ((iZIndex == 0) || (iZIndex >= sortZoneVec.size())) {
      iZIndex = (sortZoneVec.size() - 1);

      sendTo(fmt("Requested zone index is out of range.  Range is: 1, ..., %u\n\r") % iZIndex);
    } else
      doZonesSingle(sortZoneVec[(iZIndex - 1)].zoneName);

    return;
  }

  tStBuffer += tBuffer;

  if (iZone >= 0 && iZone < zone_table.size()) {
    sprintf(tString, "zoneHelp/%d", (iZone > 0 ? (zone_table[iZone - 1].top + 1) : 0));

    if (file_to_sstring(tString, tStTemp)) {
      tStBuffer += "\n\r";
      tStBuffer += tStTemp;
    }
  }

  if (strlen(tStats)) {
    tStBuffer += "\n\r";
    tStBuffer += tStats;
  }

  desc->page_string(tStBuffer);
}

void TBeing::doZones(sstring tStString)
{
  if (!tStString.empty()) {
    doZonesSingle(tStString);
    return;
  }

  unsigned int zone;
  sstring      str;
  int          cIndex = 0;

  const char *colorStrings[] = { "<r>", "<g>", "<p>", "<k>", "<b>", "<c>", "<o>", "<z>" };

  if (!desc)
    return;

  str = "The level information presented here is based on statistical averaging of the\n\r";
  str += "creatures found therein, and may not accurately reflect the intended level\n\r";
  str += "for players.  The levels shown may also fluxuate from moment to moment.\n\r";
  str += "\n\r";

  vector<zoneSorter>sortZoneVec(0);

  for (zone = 0; zone < zone_table.size(); zone++) {
    // skip the void (0) : this is for misc. mobs
    // skip immort zone (1)
    if (zone == 0 || zone == 1)
      continue;

    zoneData & zd = zone_table[zone];

    if (!zd.enabled)
      continue;

    if (!zd.num_mobs)
      continue;

    char buf[256],
         buf2[256];

    strcpy(buf, zd.name);
    char *s = strchr(buf, '-');
    char *n = buf;

    if (s) {
      --s;       // get the space before the -
      *s = '\0'; // after builder name
      s += 2;    // get the space after the -
      *s = '\0'; // after zone name
      ++s;
    } else {
      s = buf;
      n = NULL;
    }

    // buf is now the builder name, s is the zone name
    float  avg    = (zd.num_mobs ? zd.mob_levels / zd.num_mobs : 0);
    double x,
           total  = 0,
           dev,
           minlev = 1000,
           maxlev = -1;
    int    virt,
           count  = 0;

    for(unsigned int mobnum=0;mobnum<mob_index.size();mobnum++){
      virt=mob_index[mobnum].virt;

      if (virt > zone_table[zone-1].top && virt <=  zone_table[zone].top && mob_index[mobnum].doesLoad) {
	count += mob_index[mobnum].getMaxNumber();
	x      = (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());

	if (mob_index[mobnum].level>maxlev)
	  maxlev = mob_index[mobnum].level;

	if (mob_index[mobnum].level<minlev)
	  minlev = mob_index[mobnum].level;

	x     -= avg;
	x     *= x;
	total += x;

      }
    }

    total /= (count-1);
    dev    = sqrt(total);
    total  = count = 0;

    // now go through the mobs again and average up only a few
    for (unsigned int mobnum = 0; mobnum < mob_index.size(); mobnum++) {
      virt = mob_index[mobnum].virt;

      if (virt > zone_table[zone-1].top && virt <= zone_table[zone].top && mob_index[mobnum].doesLoad) {
	if (mob_index[mobnum].level < avg+dev && mob_index[mobnum].level > avg-dev) {
	  total += (mob_index[mobnum].level * mob_index[mobnum].getMaxNumber());
	  count += mob_index[mobnum].getMaxNumber();
	}
      }
    }

    if(count > 0 && total > 0)
      avg = total / count;

    if(minlev == 1000)
      minlev = zd.min_mob_level;

    if(maxlev == -1)
      maxlev = zd.max_mob_level;

    sprintf(buf2, "%-25.25s : %-10.10s : Level: avg: %i, min: %3.0f, max %3.0f\n\r", s, (n ? n : ""), (int)avg, minlev, maxlev);

    sortZoneVec.push_back(zoneSorter(avg, buf2));
  }

  // sort the vector
  sort(sortZoneVec.begin(), sortZoneVec.end(), zoneSorter());

  // list is sorted by avg level, cat it all together now
  int lastavg = -1;

  for (zone = 0; zone < sortZoneVec.size(); zone++) {
    char tString[256];

    if ((int)(sortZoneVec[zone].avgLevel) != lastavg) {
      if (++cIndex == 8)
	cIndex = 0;

      lastavg = (int)(sortZoneVec[zone].avgLevel);
    }

    sprintf(tString, "%d. %s%s<z>", (zone + 1), colorStrings[cIndex], sortZoneVec[zone].zoneName.c_str());
    str += tString;
  }

  desc->page_string(str);
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1996, SneezyMUD Coding Team
// All rights reserved.
//
// "materials.cc" - Various functions related to materials.
//
// $Log: materials.cc,v $
// Revision 1.2  1999/09/13 13:52:13  batopr
// *** empty log message ***
//
// Revision 1.1  1999/09/12 17:24:04  sneezy
//
//////////////////////////////////////////////////////////////////////////

#include "stdsneezy.h"
#include "statistics.h"

static bool genericDamCheck(int susc, int sharp)
{
  int chance = 1000 - (int) (1000 * gold_modifier[GOLD_REPAIR]);
  if (::number(0,999) < chance)
    return false;

  // num is the hardness of what i a hitting
  // susc is the hardness of the hitter
  // for the NPC vs PC situation, sharp comes from sharpness[] which
  // we can roughly figure is "30"
  // we should shoot for the randomness being roughly what the susc would
  // be : implying (num + susc) > number(30,130)
  // but make the chance fairly high and let the global modifier above
  // suck up the slack to make economy come out right
  // the min(30, sharp) case is to keep PC v NPC from shredding equipment
  if (sharp && susc && ((min(30, sharp) + susc) > ::number(20, 120)))
    return TRUE;

  return false;
}

bool TObj::willDent(int num)
{
  int susc = material_nums[getMaterial()].smash_susc;
  return genericDamCheck(susc, num);
}

bool TObj::willTear(int num)
{
  int susc = material_nums[getMaterial()].cut_susc;
  return genericDamCheck(susc, num);
}

bool TObj::willPuncture(int num)
{
  int susc = material_nums[getMaterial()].pierced_susc;
  return genericDamCheck(susc, num);
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::dentItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int hardness;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  hardness = equipment[slot] ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willDent(hardness)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You chip your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You chip $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You dent your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You dent $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }

    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::tearItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int sharp = 0;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  sharp = (equipment[slot] && !dynamic_cast<TBaseWeapon *>(equipment[slot])) ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willTear(sharp)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You scratch your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You scratch $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You tear your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You tear $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }
    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::pierceItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int sharp;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  sharp = (equipment[slot] && !dynamic_cast<TBaseWeapon *>(equipment[slot])) ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willPuncture(sharp)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You crack your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You crack $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You puncture your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You puncture $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }

    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

bool TThing::isMetal() const
{
  switch (getMaterial()) {
    case MAT_GEN_METAL:
    case MAT_COPPER:
    case MAT_BANDED_MAIL:
    case MAT_CHAIN_MAIL:
    case MAT_PLATE:
    case MAT_BRONZE:
    case MAT_BRASS:
    case MAT_IRON:
    case MAT_STEEL:
    case MAT_SILVER:
    case MAT_GOLD:
    case MAT_PLATINUM:
    case MAT_TITANIUM:
    case MAT_ALUMINUM:
    case MAT_RINGMAIL:
    case MAT_GNOMEMAIL:
    case MAT_ELECTRUM:
    case MAT_ATHANOR:
    case MAT_TIN:
    case MAT_TERBIUM:
      return TRUE;
    default:
      return FALSE;
  }
}

bool TThing::isMineral() const
{
  switch (getMaterial()) {
    case MAT_GLASS:
    case MAT_PORCELAIN:
    case MAT_GEN_MINERAL:
    case MAT_JEWELED:
    case MAT_RUNED:
    case MAT_CRYSTAL:
    case MAT_DIAMOND:
    case MAT_EBONY:
    case MAT_EMERALD:
    case MAT_IVORY:
    case MAT_OBSIDIAN:
    case MAT_ONYX:
    case MAT_OPAL:
    case MAT_RUBY:
    case MAT_SAPPHIRE:
    case MAT_MARBLE:
    case MAT_STONE:
    case MAT_BONE:
    case MAT_JADE:
    case MAT_AMBER:
    case MAT_TURQUOISE:
    case MAT_AMETHYST:
    case MAT_MICA:
    case MAT_DRAGONBONE:
    case MAT_MALACHITE:
    case MAT_GRANITE:
    case MAT_MITHRIL:
    case MAT_ADAMANTITE:
      return TRUE;
    default:
      return FALSE;
  }
}

bool TObj::canRust()
{
  if (getMaxStructPoints() < 0)
    return FALSE;
  if (getStructPoints() < 0)
    return FALSE;
  if (material_nums[getMaterial()].acid_susc <= 0)
    return FALSE;
  if (!isMetal())
    return FALSE;
  switch (getMaterial()) {
    case MAT_CLOTH:
    case MAT_SILK:
      return FALSE;
  }

  return TRUE;
}
<commit_msg>removed gold_modifier[GOLD_REPAIR] from damcheck formula<commit_after>//////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1996, SneezyMUD Coding Team
// All rights reserved.
//
// "materials.cc" - Various functions related to materials.
//
// $Log: materials.cc,v $
// Revision 1.3  1999/09/30 17:28:52  batopr
// removed gold_modifier[GOLD_REPAIR] from damcheck formula
//
// Revision 1.2  1999/09/13 13:52:13  batopr
// *** empty log message ***
//
// Revision 1.1  1999/09/12 17:24:04  sneezy
//
//////////////////////////////////////////////////////////////////////////

#include "stdsneezy.h"
#include "statistics.h"

static bool genericDamCheck(int susc, int sharp)
{
#if 0
  // gold_repair is taken into account in the cost to repair stuff formula
  // this is probably obsolete - 9/30/99
  int chance = 1000 - (int) (1000 * gold_modifier[GOLD_REPAIR]);
  if (::number(0,999) < chance)
    return false;
#endif

  // num is the hardness of what i a hitting
  // susc is the hardness of the hitter
  // for the NPC vs PC situation, sharp comes from sharpness[] which
  // we can roughly figure is "30"
  // we should shoot for the randomness being roughly what the susc would
  // be : implying (num + susc) > number(30,130)
  // but make the chance fairly high and let the global modifier above
  // suck up the slack to make economy come out right
  // the min(30, sharp) case is to keep PC v NPC from shredding equipment
  if (sharp && susc && ((min(30, sharp) + susc) > ::number(20, 120)))
    return TRUE;

  return false;
}

bool TObj::willDent(int num)
{
  int susc = material_nums[getMaterial()].smash_susc;
  return genericDamCheck(susc, num);
}

bool TObj::willTear(int num)
{
  int susc = material_nums[getMaterial()].cut_susc;
  return genericDamCheck(susc, num);
}

bool TObj::willPuncture(int num)
{
  int susc = material_nums[getMaterial()].pierced_susc;
  return genericDamCheck(susc, num);
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::dentItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int hardness;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  hardness = equipment[slot] ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willDent(hardness)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You chip your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You chip $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s chipped.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You dent your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You dent $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s dented.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }

    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::tearItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int sharp = 0;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  sharp = (equipment[slot] && !dynamic_cast<TBaseWeapon *>(equipment[slot])) ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willTear(sharp)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You scratch your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You scratch $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s scratched.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You tear your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You tear $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s torn.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }
    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

// item should be the item being dented (victim's)
// slot should be the bodyslot that is doing the denting (caster's)
int TBeing::pierceItem(TBeing *victim, TObj *item, int amt, int slot)
{
  char buf[256];
  int sharp;

  // Don't damage equipment in arena fights.
  if (victim->roomp && victim->roomp->isRoomFlag(ROOM_ARENA))
    return FALSE;

  // use hardness of eq on slot, or use skintype for hardness
  sharp = (equipment[slot] && !dynamic_cast<TBaseWeapon *>(equipment[slot])) ?
     material_nums[(equipment[slot])->getMaterial()].hardness :
     material_nums[getMaterial()].hardness;

  if (item->willPuncture(sharp)) {
    if (item->isMineral()) {
      if (item->thingHolding() == this) {
        act("You crack your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You crack $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s cracked.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    } else {
      if (item->thingHolding() == this) {
        act("You puncture your $o.", TRUE, this, item, 0, TO_CHAR);

        sprintf(buf, "$n's $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, 0, TO_ROOM);
      } else {
        act("You puncture $S $o.", TRUE, this, item, victim, TO_CHAR);

        sprintf(buf, "Your $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_VICT, ANSI_RED);

        sprintf(buf, "$N's $o %s punctured.", item->isPaired() ? "are" : "is");
        act(buf, TRUE, this, item, victim, TO_NOTVICT);
      }
    }

    item->addToStructPoints(-amt);
    if (item->getStructPoints() <= 0) {
      item->makeScraps();
      return DELETE_ITEM;
    }
    return TRUE;
  }
  return FALSE;
}

bool TThing::isMetal() const
{
  switch (getMaterial()) {
    case MAT_GEN_METAL:
    case MAT_COPPER:
    case MAT_BANDED_MAIL:
    case MAT_CHAIN_MAIL:
    case MAT_PLATE:
    case MAT_BRONZE:
    case MAT_BRASS:
    case MAT_IRON:
    case MAT_STEEL:
    case MAT_SILVER:
    case MAT_GOLD:
    case MAT_PLATINUM:
    case MAT_TITANIUM:
    case MAT_ALUMINUM:
    case MAT_RINGMAIL:
    case MAT_GNOMEMAIL:
    case MAT_ELECTRUM:
    case MAT_ATHANOR:
    case MAT_TIN:
    case MAT_TERBIUM:
      return TRUE;
    default:
      return FALSE;
  }
}

bool TThing::isMineral() const
{
  switch (getMaterial()) {
    case MAT_GLASS:
    case MAT_PORCELAIN:
    case MAT_GEN_MINERAL:
    case MAT_JEWELED:
    case MAT_RUNED:
    case MAT_CRYSTAL:
    case MAT_DIAMOND:
    case MAT_EBONY:
    case MAT_EMERALD:
    case MAT_IVORY:
    case MAT_OBSIDIAN:
    case MAT_ONYX:
    case MAT_OPAL:
    case MAT_RUBY:
    case MAT_SAPPHIRE:
    case MAT_MARBLE:
    case MAT_STONE:
    case MAT_BONE:
    case MAT_JADE:
    case MAT_AMBER:
    case MAT_TURQUOISE:
    case MAT_AMETHYST:
    case MAT_MICA:
    case MAT_DRAGONBONE:
    case MAT_MALACHITE:
    case MAT_GRANITE:
    case MAT_MITHRIL:
    case MAT_ADAMANTITE:
      return TRUE;
    default:
      return FALSE;
  }
}

bool TObj::canRust()
{
  if (getMaxStructPoints() < 0)
    return FALSE;
  if (getStructPoints() < 0)
    return FALSE;
  if (material_nums[getMaterial()].acid_susc <= 0)
    return FALSE;
  if (!isMetal())
    return FALSE;
  switch (getMaterial()) {
    case MAT_CLOTH:
    case MAT_SILK:
      return FALSE;
  }

  return TRUE;
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//////////////////////////////////////////////////////////////////////////

#include "stdsneezy.h"
#include "obj_plant.h"

TPlant::TPlant() :
  TExpandableContainer()
{
  setType(0);
  setAge(0);
  setYield(0);
  setCarryWeightLimit(100.0);
  setCarryVolumeLimit(10000);
}

TPlant::TPlant(const TPlant &a) :
  TExpandableContainer(a)
{
}

void TPlant::assignFourValues(int x1, int x2, int x3, int x4)
{
  setType(x1);
  setAge(x2);
  setYield(x3);
  setVerminated(x4);
}

void TPlant::getFourValues(int *x1, int *x2, int *x3, int *x4) const
{
  *x1 = getType();
  *x2 = getAge();
  *x3 = getYield();
  *x4 = getVerminated();
}

TPlant & TPlant::operator=(const TPlant &a)
{
  if (this == &a) return *this;
  TObj::operator=(a);
  return *this;
}

TPlant::~TPlant()
{
  if (this->getVerminated())
	  vlogf(LOG_MISC, fmt("%s is dying: lifetime yield of %i, %i lost to vermin.") % this->getName() % this->getYield() % this->getVerminated());
  else
	  vlogf(LOG_MISC, fmt("%s is dying: lifetime yield of %i.") % this->getName() % this->getYield());
}

int TPlant::putSomethingInto(TBeing *ch, TThing *)
{
  // technically, would be OK since is a container, but prevent them anyhow
  act("Unfortunately, you can't put things into $p.",
             FALSE, ch, this, 0, TO_CHAR);
  return 2;
}

sstring TPlant::statObjInfo() const
{
  char buf[256];
  if (this->getVerminated())
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i To Vermin: %i", getType(), getAge(), getYield(), getVerminated());
  else
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i", getType(), getAge(), getYield());
  sstring a(buf);
  return a;
}

// the case numbers are the vnum of the seed tool object
// the return value is the plant type index
int seed_to_plant(int vnum)
{
  switch(vnum){
    case 13880:
      return 0;
    case 13881:
      return 1;
    case 13882:
      return 2;
    case 13883:
      return 3;
    case 13884:
      return 4;
    case 13885:
      return 5;
    case 13886:
      return 6;
    case 34213:
      return 7;
    case 33526:
      return 8;
    case 35485:
      return 9;
    case 35499:
      return 10;
    case 33600:
      return 11;
    case 34738:
      return 12;
    case 29410:  // candy heart seed
      return 13; // candy heart tree
    case 34216:  // grape seeds
      return 14; // gray grapes
  }

  return 0;
}

void TPlant::updateDesc()
{
  int plantindex=min(3, (getAge()/10));
  char buf[256];

  const char *plantkeywords [] =
  {
    "mound dirt",
    "sprout tiny",
    "plant small %s",
    "plant %s",
    "plant withered %s"
  };

  const char *plantname [] =
  {
    "a small mound of <o>dirt<1>",
    "a tiny sprout",
    "a small %s",
    "a %s",
    "an old, withered %s"
  };

  const char *plantdesc [] =
  {
    "A small mound of <o>dirt<1> is here.",
    "A tiny sprout is growing here.",
    "A small %s is here.",
    "A %s is here.",
    "An old, withered %s is here."
  };

  const char *planttypes [] =
  {
    "<r>tomato<1> <g>plant<1>",
    "<r>red<1> rose bush",
    "<r>apple<1> tree",
    "<W>white<1> rose bush",
    "<Y>yellow<1> rose bush",
    "<o>orange<1> tree",
    "<g>money<1> tree",
    "<w>pipe<g>weed<1><o> bush<1>",
    "<o>pumpkin<1> vine",
    "<g>turnip<1> plant",
    "<G>lettuce<1> plant",
    "<g>marijuana<1> plant",
    "
catnip<1> <g>plant<1>",
    "
candy heart<1> tree",
    "vine of <k>gray grapes<1>"
  };
  const char *planttypeskeywords [] =
  {
    "tomato",
    "red rose bush",
    "apple tree",
    "white rose bush",
    "yellow rose bush",
    "orange tree",
    "money tree",
    "pipeweed bush",
    "pumpkin vine",
    "turnip plant",
    "lettuce plant",
    "marijuana plant",
    "catnip plant",
    "candy heart tree",
    "gray grape vine"
  };
  int plantfruits [] =
  {
    14348,
    28917,
    8936,
    28918,
    28919,
    432,
    13,
    34212,
    33507,
    33508,
    33525,
    33601,
    34737,
    29405,
    34215
  };

  // considering a plant ages an average of 2 'age' points each mud hour,
  // a plant will age to 17520 in a mud year
  int plantlifeexpectancy [] =
  {
    175200, // 10 years
    262800, // 15 years
    219000,  // 12.5 years
    262800,  // 15 years
    262800,  // 15 years
    219000,  // 12.5 years
    35040,  // 2 years
    17520,  // 1 year
    8760, // 1/2 year
    8760, // 1/2 year
    8760, // bah
    17520, // 1 year
    17520, // 1 year
    2350, // 2 real days
    262800 // 15 years
  };

  // really old plants should wither and die
  if(getAge() > plantlifeexpectancy[getType()]){
    plantindex=4;
    if(obj_flags.decay_time <= -1)
      obj_flags.decay_time=300;
  }

  if (isObjStat(ITEM_STRUNG)) {
    delete [] shortDescr;
    delete [] descr;

    extraDescription *exd;
    while ((exd = ex_description)) {
      ex_description = exd->next;
      delete exd;
    }
    ex_description = NULL;
    delete [] action_description;
    action_description = NULL;
  } else {
    addObjStat(ITEM_STRUNG);
    name = mud_str_dup(obj_index[getItemIndex()].name);
    ex_description = NULL;
    action_description = NULL;
  }

  if(plantindex>=2){
    sprintf(buf, plantkeywords[plantindex], planttypeskeywords[getType()]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex], planttypes[getType()]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex], planttypes[getType()]);
    setDescr(mud_str_dup(buf));
  } else {
    sprintf(buf, plantkeywords[plantindex]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex]);
    setDescr(mud_str_dup(buf));
  }

  if(plantindex == 3 && !::number(0,3)){
    TThing *t;
    int count=0;

    for(t=getStuff();t;t=t->nextThing){
      ++count;
    }

    if(count<=4){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(plantindex == 4 && !::number(0,11)){
    TThing *t;
    int count=0;

    for(t=getStuff();t;t=t->nextThing){
      ++count;
    }

    if(count<=2){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if (roomp && getType() == 13 && plantindex > 1) {
	  // candy trees spawn rainbow-rats, which eat the candy
	  if (plantindex > 2) {
		  TThing *tcount;
		  TPlant *tree;
		  double tree_count = 0.0;
		  for(tcount = roomp->getStuff(); tcount; tcount = tcount->nextThing) {
			tree = dynamic_cast<TPlant *>(tcount);
		    if(tree && tree->getType() == getType()) {
		      // looking for same type of trees
		      // let's only count trees with stuff in them, to avoid over-population of vermin
		      if (tree->getStuff())
		    	  ++tree_count;
		    }
		  }
		  if (tree_count > 1 && ::number(1, 100000) < (int) ((tree_count / 16) * 200)) {
			// this is about a 17% chance per MUD day for a room of 8 laden trees
			// 5% for a room of 2 trees
			int mob_vnum = MOB_CANDY_HEART_EATER; // sugar-toothed weasel
		    TBeing *mob = read_mobile(mob_vnum, VIRTUAL);
		    if (!mob) {
		      // vlogf(LOG_PROC, fmt("Failed attempt to spawn a fruit scavenger (%i) in room %i.") % mob_vnum % in_room);
		      return;
		    }
		    *roomp += *mob;
		    colorAct(COLOR_MOBS,
		            ((mob->ex_description && mob->ex_description->findExtraDesc("repop")) ?
		            mob->ex_description->findExtraDesc("repop") :
		            "$n appears suddenly in the room."), TRUE, mob, 0, 0, TO_ROOM);
		  }
	  }
  }
}

void TPlant::updateAge(){
  setAge(getAge()+::number(1,3));
  updateDesc();
  if(roomp)
    roomp->saveItems("");
}

<commit_msg>Removed the mob spawning thing from plants.<commit_after>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//////////////////////////////////////////////////////////////////////////

#include "stdsneezy.h"
#include "obj_plant.h"

TPlant::TPlant() :
  TExpandableContainer()
{
  setType(0);
  setAge(0);
  setYield(0);
  setCarryWeightLimit(100.0);
  setCarryVolumeLimit(10000);
}

TPlant::TPlant(const TPlant &a) :
  TExpandableContainer(a)
{
}

void TPlant::assignFourValues(int x1, int x2, int x3, int x4)
{
  setType(x1);
  setAge(x2);
  setYield(x3);
  setVerminated(x4);
}

void TPlant::getFourValues(int *x1, int *x2, int *x3, int *x4) const
{
  *x1 = getType();
  *x2 = getAge();
  *x3 = getYield();
  *x4 = getVerminated();
}

TPlant & TPlant::operator=(const TPlant &a)
{
  if (this == &a) return *this;
  TObj::operator=(a);
  return *this;
}

TPlant::~TPlant()
{
  if (this->getVerminated())
	  vlogf(LOG_MISC, fmt("%s is dying: lifetime yield of %i, %i lost to vermin.") % this->getName() % this->getYield() % this->getVerminated());
  else
	  vlogf(LOG_MISC, fmt("%s is dying: lifetime yield of %i.") % this->getName() % this->getYield());
}

int TPlant::putSomethingInto(TBeing *ch, TThing *)
{
  // technically, would be OK since is a container, but prevent them anyhow
  act("Unfortunately, you can't put things into $p.",
             FALSE, ch, this, 0, TO_CHAR);
  return 2;
}

sstring TPlant::statObjInfo() const
{
  char buf[256];
  if (this->getVerminated())
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i To Vermin: %i", getType(), getAge(), getYield(), getVerminated());
  else
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i", getType(), getAge(), getYield());
  sstring a(buf);
  return a;
}

// the case numbers are the vnum of the seed tool object
// the return value is the plant type index
int seed_to_plant(int vnum)
{
  switch(vnum){
    case 13880:
      return 0;
    case 13881:
      return 1;
    case 13882:
      return 2;
    case 13883:
      return 3;
    case 13884:
      return 4;
    case 13885:
      return 5;
    case 13886:
      return 6;
    case 34213:
      return 7;
    case 33526:
      return 8;
    case 35485:
      return 9;
    case 35499:
      return 10;
    case 33600:
      return 11;
    case 34738:
      return 12;
    case 29410:  // candy heart seed
      return 13; // candy heart tree
    case 34216:  // grape seeds
      return 14; // gray grapes
  }

  return 0;
}

void TPlant::updateDesc()
{
  int plantindex=min(3, (getAge()/10));
  char buf[256];

  const char *plantkeywords [] =
  {
    "mound dirt",
    "sprout tiny",
    "plant small %s",
    "plant %s",
    "plant withered %s"
  };

  const char *plantname [] =
  {
    "a small mound of <o>dirt<1>",
    "a tiny sprout",
    "a small %s",
    "a %s",
    "an old, withered %s"
  };

  const char *plantdesc [] =
  {
    "A small mound of <o>dirt<1> is here.",
    "A tiny sprout is growing here.",
    "A small %s is here.",
    "A %s is here.",
    "An old, withered %s is here."
  };

  const char *planttypes [] =
  {
    "<r>tomato<1> <g>plant<1>",
    "<r>red<1> rose bush",
    "<r>apple<1> tree",
    "<W>white<1> rose bush",
    "<Y>yellow<1> rose bush",
    "<o>orange<1> tree",
    "<g>money<1> tree",
    "<w>pipe<g>weed<1><o> bush<1>",
    "<o>pumpkin<1> vine",
    "<g>turnip<1> plant",
    "<G>lettuce<1> plant",
    "<g>marijuana<1> plant",
    "
catnip<1> <g>plant<1>",
    "
candy heart<1> tree",
    "vine of <k>gray grapes<1>"
  };
  const char *planttypeskeywords [] =
  {
    "tomato",
    "red rose bush",
    "apple tree",
    "white rose bush",
    "yellow rose bush",
    "orange tree",
    "money tree",
    "pipeweed bush",
    "pumpkin vine",
    "turnip plant",
    "lettuce plant",
    "marijuana plant",
    "catnip plant",
    "candy heart tree",
    "gray grape vine"
  };
  int plantfruits [] =
  {
    14348,
    28917,
    8936,
    28918,
    28919,
    432,
    13,
    34212,
    33507,
    33508,
    33525,
    33601,
    34737,
    29405,
    34215
  };

  // considering a plant ages an average of 2 'age' points each mud hour,
  // a plant will age to 17520 in a mud year
  int plantlifeexpectancy [] =
  {
    175200, // 10 years
    262800, // 15 years
    219000,  // 12.5 years
    262800,  // 15 years
    262800,  // 15 years
    219000,  // 12.5 years
    35040,  // 2 years
    17520,  // 1 year
    8760, // 1/2 year
    8760, // 1/2 year
    8760, // bah
    17520, // 1 year
    17520, // 1 year
    2350, // 2 real days
    262800 // 15 years
  };

  // really old plants should wither and die
  if(getAge() > plantlifeexpectancy[getType()]){
    plantindex=4;
    if(obj_flags.decay_time <= -1)
      obj_flags.decay_time=300;
  }

  if (isObjStat(ITEM_STRUNG)) {
    delete [] shortDescr;
    delete [] descr;

    extraDescription *exd;
    while ((exd = ex_description)) {
      ex_description = exd->next;
      delete exd;
    }
    ex_description = NULL;
    delete [] action_description;
    action_description = NULL;
  } else {
    addObjStat(ITEM_STRUNG);
    name = mud_str_dup(obj_index[getItemIndex()].name);
    ex_description = NULL;
    action_description = NULL;
  }

  if(plantindex>=2){
    sprintf(buf, plantkeywords[plantindex], planttypeskeywords[getType()]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex], planttypes[getType()]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex], planttypes[getType()]);
    setDescr(mud_str_dup(buf));
  } else {
    sprintf(buf, plantkeywords[plantindex]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex]);
    setDescr(mud_str_dup(buf));
  }

  if(plantindex == 3 && !::number(0,3)){
    TThing *t;
    int count=0;

    for(t=getStuff();t;t=t->nextThing){
      ++count;
    }

    if(count<=4){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(plantindex == 4 && !::number(0,11)){
    TThing *t;
    int count=0;

    for(t=getStuff();t;t=t->nextThing){
      ++count;
    }

    if(count<=2){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }
  /*
  if (roomp && getType() == 13 && plantindex > 1) {
	  // candy trees spawn rainbow-rats, which eat the candy
	  if (plantindex > 2) {
		  TThing *tcount;
		  TPlant *tree;
		  double tree_count = 0.0;
		  for(tcount = roomp->getStuff(); tcount; tcount = tcount->nextThing) {
			tree = dynamic_cast<TPlant *>(tcount);
		    if(tree && tree->getType() == getType()) {
		      // looking for same type of trees
		      // let's only count trees with stuff in them, to avoid over-population of vermin
		      if (tree->getStuff())
		    	  ++tree_count;
		    }
		  }
		  if (tree_count > 1 && ::number(1, 100000) < (int) ((tree_count / 16) * 200)) {
			// this is about a 17% chance per MUD day for a room of 8 laden trees
			// 5% for a room of 2 trees
			int mob_vnum = MOB_CANDY_HEART_EATER; // sugar-toothed weasel
		    TBeing *mob = read_mobile(mob_vnum, VIRTUAL);
		    if (!mob) {
		      // vlogf(LOG_PROC, fmt("Failed attempt to spawn a fruit scavenger (%i) in room %i.") % mob_vnum % in_room);
		      return;
		    }
		    *roomp += *mob;
		    colorAct(COLOR_MOBS,
		            ((mob->ex_description && mob->ex_description->findExtraDesc("repop")) ?
		            mob->ex_description->findExtraDesc("repop") :
		            "$n appears suddenly in the room."), TRUE, mob, 0, 0, TO_ROOM);
		  }
	  }
  }
  */
}

void TPlant::updateAge(){
  setAge(getAge()+::number(1,3));
  updateDesc();
  if(roomp)
    roomp->saveItems("");
}

<|endoftext|>