<commit_before>/**
	maintain game logic and supervise game objects
	
	@namespace GAS
	@class game
**/

GAS.game = {

	scene: 0,

	/**
		initialize game objects
		
		@method init
	**/
	
	init: function() {
		this.weed.init();
		this.npc.init();
	},
	
	/**
		updates not handled by individual objects
		called on every frame
		
		@method update
	**/
	
	update: function() {
		if (this.trackNpc) {
			this.npc.tracking();
		}
		if (this.mini.game) {
			this.mini.update();
		}
	},
	
	/**
		lazy updates not handled by individual objects
		called on every display list cleanup (1-2 seconds)
		
		@method nudge
	**/
	
	nudge: function() {
	},
		
	/**
		stage the current scene in the plot
		and advance to the next scene
		
		@method advance
	**/
	
	advance: function() {
		var scene = GAS.lookup.plot[this.scene];
		var cast = GAS.lookup.cast;
		var actor, p, r, a, b;
		
		// if a minigame has been set up
		if (this.mini.game) {
			// start it
			this.mini.start();
			// and bug out
			return;
		}
		
		//console.log(JSON.stringify(scene));
		
		// if there's prose in the scene
		if (scene.prose) {
			// display it
			GAS.hud.showProse(scene.prose, true);
			// if there's an active NPC, get them talking
			if (this.activeNpc) {
				this.activeNpc.talking = true;
			}
		} else {
			// nope, clean up after the last one
			GAS.hud.showProse();
		}
		
		// if a character is to be distanced from the player
		if (scene.warp) {
			actor = cast[scene.warp].model;
			// set new position far from player
			p = GAS.player.position;
			r = GAS.map.RADIUS;
			actor.position.set(r - p.x, r - p.y, r - p.z).norm().mul(r);
			// set wandering behavior
			actor.update = GAS.game.npc.wander;
		}
		
		// if a character is supposed to be upset
		if (scene.upset) {
			// store off the character object for tracking
			this.trackNpc = cast[scene.upset].model;
			// and set their behavior to shouting
			this.trackNpc.update = GAS.game.npc.shout;
		}
		
		// if a character is supposed to leave
		if (scene.leave) {
			actor = cast[scene.leave.npc].model;
			// set leaving target and behavior
			actor.behavior.target.copy(scene.leave.target);
			actor.update = GAS.game.npc.leave;
		}
		
		// if a character has calmed down
		if (scene.calmed) {
			// reset their behavior
			this.activeNpc.update = this.npc.wander;
			// shut them up
			this.activeNpc.talking = false;
			// remove the object reference
			delete this.activeNpc;
		}
		
		// if two characters are to be swapped
		if (scene.swap) {
			a = cast[scene.swap[0]].model;
			b = cast[scene.swap[1]].model;
			if (a === this.activeNpc) {
				this.activeNpc = b;
			} else if (b === this.activeNpc) {
				this.activeNpc = a;
			}
			
			p = a.position;
			a.position = b.position;
			b.position = p;
			
			p = a.update;
			a.update = b.update;
			b.update = p;
		}
		
		// if a minigame needs setting up
		if (scene.mini) {
			this.mini.setup(scene.mini, scene.prose);
		}
		
		// if the scene is to be faded in/out
		if (scene.fade) {
			GAS.hud.setFade(scene.fade.time * 1000, scene.fade.alpha);
		}
		
		// next scene
		this.scene++;
	},
	
	/**
	
		weed collection
		
	**/
	
	weed: {
	
		/**
			generate all the weeds in the map
			
			@method init
		**/
		
		init: function() {
			var rm = GAS.map.RADIUS;
			var rw = 1.25 * GAS.weeds.DRAW_RADIUS;
			var rng = SOAR.random.create(123069);
			var x, y, z, r, o;
			// for all places in the map that can hold a weed
			for (x = -rm; x <= rm; x += rw) {
				for (y = -rm; y <= rm; y += rw) {
					for (z = -rm; z <= rm; z += rw) {
						// calculate a normalized distance to the center of the map
						r = Math.sqrt(x * x + y * y + z * z) / rm;
						// if the distance conforms to a spherical volume
						if (r < 1) {
							// add a new weed
							GAS.map.add(GAS.weeds.create(x, y, z));
						}
					}
				}
			}
		}
	},
	
	
	/**
	
		paddler NPC handling and behaviors
		
	**/
	
	npc: {

		WATCH_RADIUS: 10,
		EVADE_RADIUS: 3,
		
		prompting: false,
		
		/**
			initialize the NPCs
			
			@method init
		**/

		init: function() {
			var cast = GAS.lookup.cast;
			var npcl = GAS.lookup.npc;
			var i, il, n, o;
			
			// for each NPC in the cast list
			for (i = 0, il = npcl.length; i < il; i++) {
				n = cast[ npcl[i] ];
				
				// create a paddler model
				o = GAS.paddler.create(n.skin);
				
				// start where directed
				o.position.copy(n.start);
				
				// TEMP: place in test position
//				o.position.set(GAS.rng.get(-1, 1), GAS.rng.get(-1, 1), GAS.rng.get(-1, 1)).norm().mul(10);
				o.position.set(0, 0, -2);
				
				// store off the paddler's update method
				o.updateMotion = GAS.paddler.update;
				// and set to wandering behavior
				o.update = this.wander;

				// set up remaining NPC-specific stuff
				o.name = n.name;
				o.behavior = {
					period: GAS.makeSwatch(1),
					target: SOAR.vector.create(),
				};
				o.interact = this.interact;
				
				o.playerDistance = function() {
					return this.position.distance(GAS.player.position);
				};
				
				// add model to map and character object
				GAS.map.add(o);
				n.model = o;
			}
		
			// create a marker for the NPCs
			this.marker = GAS.card.create("sound");
			this.marker.scale = 10;
			this.marker.hidden = true;
			GAS.map.add(this.marker);
		},
		
		/**
			implements wandering behavior - a slow, random
			drift through the weed cluster. this is pretty
			much the default behavior for a paddler.
			
			called in the context of the NPC
			
			@method wander
		**/
		
		wander: function() {
			var behave = this.behavior;
			var npc = GAS.game.npc;
			
			// maintain slow speed
			this.haste = 1;

			// if npc is about to exit the weed cluster
			if (this.position.length() >= GAS.map.RADIUS) {
				// point them back toward its center
				behave.target.copy(this.position).neg().norm();
			} else {
				// if it's been too long since e changed direction
				if (behave.period.read() > 1) {
					// set new target and new period between 10-20 s
					behave.period = GAS.makeSwatch(GAS.rng.get(10, 20));
					behave.target.set( GAS.rng.get(-1, 1), GAS.rng.get(-0.5, 0.5), GAS.rng.get(-1, 1) ).norm();
				}
			}
			
			// keep em pointed at the target
			this.pointTo(behave.target, 0.05);

			// if the player is too close
			if (this.playerDistance() < npc.EVADE_RADIUS) {
				// store off a restore reference
				behave.restore = npc.wander;
				// flip to evasion behavior
				this.update = npc.evade;
			}
			
			// update the paddler model itself
			this.updateMotion();
		},
		
		/**
			implements shouting behavior - the npc hurries
			around the weed cluster, demanding attention!
			
			called in the context of the npc
			
			@method shout
		**/
		
		shout: function() {
			var behave = this.behavior;
			var npc = GAS.game.npc;
			
			// maintain fast speed
			this.haste = 2;

			// if npc is about to exit the weed cluster
			if (this.position.length() >= GAS.map.RADIUS) {
				// point them back toward its center
				behave.target.copy(this.position).neg().norm();
			} else {
				// if it's been too long since e changed direction
				if (behave.period.read() > 1) {
					// set new target and new period between 2.5-5 s
					behave.period = GAS.makeSwatch(GAS.rng.get(2.5, 5));
					behave.target.set( GAS.rng.get(-1, 1), GAS.rng.get(-0.5, 0.5), GAS.rng.get(-1, 1) ).norm();
				}
			}
			
			// keep em pointed at the target
			this.pointTo(behave.target, 0.05);

			// if the player is nearby<commit_msg>Applies new stopwatch code to NPC timers.<commit_after>/**
	maintain game logic and supervise game objects
	
	@namespace GAS
	@class game
**/

GAS.game = {

	scene: 0,

	/**
		initialize game objects
		
		@method init
	**/
	
	init: function() {
		this.weed.init();
		this.npc.init();
	},
	
	/**
		updates not handled by individual objects
		called on every frame
		
		@method update
	**/
	
	update: function() {
		if (this.trackNpc) {
			this.npc.tracking();
		}
		if (this.mini.game) {
			this.mini.update();
		}
	},
	
	/**
		lazy updates not handled by individual objects
		called on every display list cleanup (1-2 seconds)
		
		@method nudge
	**/
	
	nudge: function() {
	},
		
	/**
		stage the current scene in the plot
		and advance to the next scene
		
		@method advance
	**/
	
	advance: function() {
		var scene = GAS.lookup.plot[this.scene];
		var cast = GAS.lookup.cast;
		var actor, p, r, a, b;
		
		// if a minigame has been set up
		if (this.mini.game) {
			// start it
			this.mini.start();
			// and bug out
			return;
		}
		
		//console.log(JSON.stringify(scene));
		
		// if there's prose in the scene
		if (scene.prose) {
			// display it
			GAS.hud.showProse(scene.prose, true);
			// if there's an active NPC, get them talking
			if (this.activeNpc) {
				this.activeNpc.talking = true;
			}
		} else {
			// nope, clean up after the last one
			GAS.hud.showProse();
		}
		
		// if a character is to be distanced from the player
		if (scene.warp) {
			actor = cast[scene.warp].model;
			// set new position far from player
			p = GAS.player.position;
			r = GAS.map.RADIUS;
			actor.position.set(r - p.x, r - p.y, r - p.z).norm().mul(r);
			// set wandering behavior
			actor.update = GAS.game.npc.wander;
		}
		
		// if a character is supposed to be upset
		if (scene.upset) {
			// store off the character object for tracking
			this.trackNpc = cast[scene.upset].model;
			// and set their behavior to shouting
			this.trackNpc.update = GAS.game.npc.shout;
		}
		
		// if a character is supposed to leave
		if (scene.leave) {
			actor = cast[scene.leave.npc].model;
			// set leaving target and behavior
			actor.behavior.target.copy(scene.leave.target);
			actor.update = GAS.game.npc.leave;
		}
		
		// if a character has calmed down
		if (scene.calmed) {
			// reset their behavior
			this.activeNpc.update = this.npc.wander;
			// shut them up
			this.activeNpc.talking = false;
			// remove the object reference
			delete this.activeNpc;
		}
		
		// if two characters are to be swapped
		if (scene.swap) {
			a = cast[scene.swap[0]].model;
			b = cast[scene.swap[1]].model;
			if (a === this.activeNpc) {
				this.activeNpc = b;
			} else if (b === this.activeNpc) {
				this.activeNpc = a;
			}
			
			p = a.position;
			a.position = b.position;
			b.position = p;
			
			p = a.update;
			a.update = b.update;
			b.update = p;
		}
		
		// if a minigame needs setting up
		if (scene.mini) {
			this.mini.setup(scene.mini, scene.prose);
		}
		
		// if the scene is to be faded in/out
		if (scene.fade) {
			GAS.hud.setFade(scene.fade.time * 1000, scene.fade.alpha);
		}
		
		// next scene
		this.scene++;
	},
	
	/**
	
		weed collection
		
	**/
	
	weed: {
	
		/**
			generate all the weeds in the map
			
			@method init
		**/
		
		init: function() {
			var rm = GAS.map.RADIUS;
			var rw = 1.25 * GAS.weeds.DRAW_RADIUS;
			var rng = SOAR.random.create(123069);
			var x, y, z, r, o;
			// for all places in the map that can hold a weed
			for (x = -rm; x <= rm; x += rw) {
				for (y = -rm; y <= rm; y += rw) {
					for (z = -rm; z <= rm; z += rw) {
						// calculate a normalized distance to the center of the map
						r = Math.sqrt(x * x + y * y + z * z) / rm;
						// if the distance conforms to a spherical volume
						if (r < 1) {
							// add a new weed
							GAS.map.add(GAS.weeds.create(x, y, z));
						}
					}
				}
			}
		}
	},
	
	
	/**
	
		paddler NPC handling and behaviors
		
	**/
	
	npc: {

		WATCH_RADIUS: 10,
		EVADE_RADIUS: 3,
		
		prompting: false,
		
		/**
			initialize the NPCs
			
			@method init
		**/

		init: function() {
			var cast = GAS.lookup.cast;
			var npcl = GAS.lookup.npc;
			var i, il, n, o;
			
			// for each NPC in the cast list
			for (i = 0, il = npcl.length; i < il; i++) {
				n = cast[ npcl[i] ];
				
				// create a paddler model
				o = GAS.paddler.create(n.skin);
				
				// start where directed
				o.position.copy(n.start);
				
				// TEMP: place in test position
//				o.position.set(GAS.rng.get(-1, 1), GAS.rng.get(-1, 1), GAS.rng.get(-1, 1)).norm().mul(10);
				o.position.set(0, 0, -2);
				
				// store off the paddler's update method
				o.updateMotion = GAS.paddler.update;
				// and set to wandering behavior
				o.update = this.wander;

				// set up remaining NPC-specific stuff
				o.name = n.name;
				o.behavior = {
					period: GAS.makeSwatch(1),
					target: SOAR.vector.create(),
				};
				o.interact = this.interact;
				
				o.playerDistance = function() {
					return this.position.distance(GAS.player.position);
				};
				
				// add model to map and character object
				GAS.map.add(o);
				n.model = o;
			}
		
			// create a marker for the NPCs
			this.marker = GAS.card.create("sound");
			this.marker.scale = 10;
			this.marker.hidden = true;
			GAS.map.add(this.marker);
		},
		
		/**
			implements wandering behavior - a slow, random
			drift through the weed cluster. this is pretty
			much the default behavior for a paddler.
			
			called in the context of the NPC
			
			@method wander
		**/
		
		wander: function() {
			var behave = this.behavior;
			var npc = GAS.game.npc;
			
			// maintain slow speed
			this.haste = 1;

			// if npc is about to exit the weed cluster
			if (this.position.length() >= GAS.map.RADIUS) {
				// point them back toward its center
				behave.target.copy(this.position).neg().norm();
			} else {
				// if it's been too long since e changed direction
				if (behave.period.read() > 1) {
					// set new target and new period between 10-20 s
					behave.period.reset( GAS.rng.get(10, 20) );
					behave.target.set( GAS.rng.get(-1, 1), GAS.rng.get(-0.5, 0.5), GAS.rng.get(-1, 1) ).norm();
				}
			}
			
			// keep em pointed at the target
			this.pointTo(behave.target, 0.05);

			// if the player is too close
			if (this.playerDistance() < npc.EVADE_RADIUS) {
				// store off a restore reference
				behave.restore = npc.wander;
				// flip to evasion behavior
				this.update = npc.evade;
			}
			
			// update the paddler model itself
			this.updateMotion();
		},
		
		/**
			implements shouting behavior - the npc hurries
			around the weed cluster, demanding attention!
			
			called in the context of the npc
			
			@method shout
		**/
		
		shout: function() {
			var behave = this.behavior;
			var npc = GAS.game.npc;
			
			// maintain fast speed
			this.haste = 2;

			// if npc is about to exit the weed cluster
			if (this.position.length() >= GAS.map.RADIUS) {
				// point them back toward its center
				behave.target.copy(this.position).neg().norm();
			} else {
				// if it's been too long since e changed direction
				if (behave.period.read() > 1) {
					// set new target and new period between 2.5-5 s
					behave.period.reset( GAS.rng.get(2.5, 5) );
					behave.target.set( GAS.rng.get(-1, 1), GAS.rng.get(-0.5, 0.5), GAS.rng.get(-1, 1) ).norm();
				}
			}
			
			// keep em pointed at the target
			this.pointTo(behave.target, 0.05);

			// if the player is nearby
{'fraction_non_alphanumeric': 0.1374882345031599, 'fraction_numerical': 0.010353637219308862, 'mean_word_length': 2.148814563928874, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7066384', 'n_tokens_mistral': 6819, 'n_tokens_neox': 5323, 'n_words': 1535}