<commit_before>'use strict'

const Effects = require('./effects').Effects;

/* Symbols for private fields of effect class */
const _id      = Symbol('id');
const _options = Symbol('options');
const _type    = Symbol('type');
const _target  = Symbol('target'); 
const _elapsed = Symbol('elapsed');
const _started = Symbol('started');
const _effect  = Symbol('effect');

/* Effect class -- instantiates new effect, can be used to check for validity of effect, can be used 
 * @param {id: string, options: object, type: string, target: NPC | Player } 
 * @return 
*/
class Effect {

  constructor({ id, options, type, target }) {
    validate(id, options, type, target);
    this[_id]      = id;
    this[_type]    = type;
    this[_target]  = target;
    this[_options] = options;
    this[_effect]  = Effects.get(target, type, options);

    this[_effect].activate();
    if (this[_options].activate) { this[_options].activate(); }
  }

  /* 
   * Called when the effect is initialized 
   * or when it is loaded back onto the player after login.
   * 1) Sets up timing if necessary.
   * 2) Sets up event listeners.
   * 3) //TODO: Sets up attribute modifiers
   */
  init() {
    const target  = this[_target];
    const options = this[_options];
    
    if (options.duration) {
      this[_started] = options.started || Date.now();
      this[_elapsed] = options.elapsed || 0;
    }
      
    const effect = this[_effect];

    target.on('quit', () => {
      this.setElapsed();
      // effect.deactivate();
    });

    const events = effect.events || {};
    
    for (let event in events) {
      const handler = events[event];
      if (!handler) { throw new ReferenceError("An event was registered for an effect, but it had no handler."); }
      target.on(event, handler);
    }

  }

  /* Get private fields... */
  getId()      { return this[_id];      }
  getOptions() { return this[_options]; }
  getType()    { return this[_type];    }
  getTarget()  { return this[_target];  }

  /* Get fields from generic effect, for player-friendly consumption. */
  getName()        { return this[_effect].name; }
  getDescription() { return this[_effect].desc; }
  getAura()        { return this[_effect].aura; }

  getDuration() { 
    return parseInt(this[_options].duration, 10) || Infinity; 
  }

  getElapsed() { 
    if (isNaN(this[_started])) { return null; }
    return Date.now() - this[_started];
  }

  // Safely returns an empty object if there are no defined modifiers.
  getModifiers() {
    return this[_effect].modifiers || {};
  }

  /* Mutators */
  setElapsed() { this[_elapsed] = this.getElapsed(); }

  /* Predicates */
  isCurrent()      { return this.isTemporary() ? this.getElapsed() < this.getDuration() : true; }
  isTemporary()    { return this.getDuration() < Infinity; }
  isValid()        { return this.isCurrent() && this.checkPredicate(); }
  
  checkPredicate() { 
    const options   = this[_options];
    const predicate = options.predicate;
    return predicate ? 
      predicate() : 
      true;
  }

  /* Proxies for effect/optional methods */
  deactivate() {
    const effect = this[_effect];
    effect.deactivate();
    if (this[_options].deactivate) { 
      this[_options].deactivate(); 
    }
    const events = effect.events || {};
    for (let event in events) {
      target.removeListener(event)
    }
  }

  flatten() {
    const savedOptions = Object.assign({}, this[_options], { started: this[_started], elapsed: this[_elapsed] }); 
    return {
      id:      this[_id],
      options: savedOptions,
      type:    this[_type],
    };
  }

}

/* Validation helper for effect construction */
const validate = (id, options, type, target) => {
  if (!id) { 
    throw new ReferenceError("Effects must have an ID to prevent stacking."); 
  }
  if (!options || !Object.keys(options).length) { 
    throw new ReferenceError("Effects must take an options object."); 
  }
  if (!type) { 
    throw new ReferenceError("Effects must have a generic effect type."); 
  }
  if (!target) { 
    throw new ReferenceError("Effects must have a target."); 
  }
  if (!(target.addEffect)) { 
    throw new TypeError("Effects can only target players or NPCs."); 
  }
}

exports.Effect = Effect;<commit_msg>Try to properly handle effects persisting after logging off.<commit_after>'use strict'

const Effects = require('./effects').Effects;

/* Symbols for private fields of effect class */
const _id      = Symbol('id');
const _options = Symbol('options');
const _type    = Symbol('type');
const _target  = Symbol('target'); 
const _elapsed = Symbol('elapsed');
const _started = Symbol('started');
const _paused  = Symbol('paused');
const _effect  = Symbol('effect');


/* Effect class -- instantiates new effect, can be used to check for validity of effect, can be used 
 * @param {id: string, options: object, type: string, target: NPC | Player } 
 * @return 
*/
class Effect {

  constructor({ id, options, type, target }) {
    validate(id, options, type, target);
    this[_id]      = id;
    this[_type]    = type;
    this[_target]  = target;
    this[_options] = options;
    this[_effect]  = Effects.get(target, type, options);
    this[_paused]  = options.paused ? Date.now() - options.paused : 0;


    this[_effect].activate();
    if (this[_options].activate) { this[_options].activate(); }
  }

  /* 
   * Called when the effect is initialized 
   * or when it is loaded back onto the player after login.
   * 1) Sets up timing if necessary.
   * 2) Sets up event listeners.
   * 3) //TODO: Sets up attribute modifiers
   */
  init() {
    const target  = this[_target];
    const options = this[_options];
    
    if (options.duration) {
      this[_started] = options.started || Date.now();
      this[_elapsed] = options.elapsed || 0;
    }
      
    const effect = this[_effect];

    target.on('quit', () => {
      this.setElapsed();
      // effect.deactivate();
    });

    const events = effect.events || {};
    
    for (let event in events) {
      const handler = events[event];
      if (!handler) { throw new ReferenceError("An event was registered for an effect, but it had no handler."); }
      target.on(event, handler);
    }

  }

  /* Get private fields... */
  getId()      { return this[_id];      }
  getOptions() { return this[_options]; }
  getType()    { return this[_type];    }
  getTarget()  { return this[_target];  }

  /* Get fields from generic effect, for player-friendly consumption. */
  getName()        { return this[_effect].name; }
  getDescription() { return this[_effect].desc; }
  getAura()        { return this[_effect].aura; }

  getDuration() { 
    return parseInt(this[_options].duration, 10) || Infinity; 
  }

  getElapsed() { 
    if (isNaN(this[_started])) { return null; }
    return Date.now() - (this[_started] + this[_paused]);
  }

  // Safely returns an empty object if there are no defined modifiers.
  getModifiers() {
    return this[_effect].modifiers || {};
  }

  /* Mutators */
  setElapsed(elapsed) { this[_elapsed] = elapsed || this.getElapsed(); }

  /* Predicates */
  isCurrent()      { return this.isTemporary() ? this.getElapsed() < this.getDuration() : true; }
  isTemporary()    { return this.getDuration() < Infinity; }
  isValid()        { return this.isCurrent() && this.checkPredicate(); }
  
  checkPredicate() { 
    const options   = this[_options];
    const predicate = options.predicate;
    return predicate ? 
      predicate() : 
      true;
  }

  /* Proxies for effect/optional methods */
  deactivate() {
    const effect = this[_effect];
    effect.deactivate();
    if (this[_options].deactivate) { 
      this[_options].deactivate(); 
    }
    const events = effect.events || {};
    for (let event in events) {
      target.removeListener(event)
    }
  }

  flatten() {
    const savedOptions = Object.assign({}, this[_options], 
      { 
        started: this[_started], 
        elapsed: this[_elapsed],
        paused:  this[_started] ? Date.now() : null
      }); 
    return {
      id:      this[_id],
      options: savedOptions,
      type:    this[_type],
    };
  }

}

/* Validation helper for effect construction */
const validate = (id, options, type, target) => {
  if (!id) { 
    throw new ReferenceError("Effects must have an ID to prevent stacking."); 
  }
  if (!options || !Object.keys(options).length) { 
    throw new ReferenceError("Effects must take an options object."); 
  }
  if (!type) { 
    throw new ReferenceError("Effects must have a generic effect type."); 
  }
  if (!target) { 
    throw new ReferenceError("Effects must have a target."); 
  }
  if (!(target.addEffect)) { 
    throw new TypeError("Effects can only target players or NPCs."); 
  }
}

exports.Effect = Effect;
{'fraction_non_alphanumeric': 0.14204221334854536, 'fraction_numerical': 0.0014831717056474614, 'mean_word_length': 2.851493848857645, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6365362', 'n_tokens_mistral': 2696, 'n_tokens_neox': 2550, 'n_words': 751}