Skip to content
Snippets Groups Projects
Commit 32eb9f9d authored by svornost's avatar svornost
Browse files

Add a new pure JS random event framework and integrate it into the existing random event system.

parent f1d061ff
No related branches found
No related tags found
No related merge requests found
...@@ -17,6 +17,7 @@ App.Encyclopedia = {}; ...@@ -17,6 +17,7 @@ App.Encyclopedia = {};
App.Encyclopedia.Entries = {}; App.Encyclopedia.Entries = {};
App.Entity = {}; App.Entity = {};
App.Entity.Utils = {}; App.Entity.Utils = {};
App.Events = {};
App.MainView = {}; App.MainView = {};
App.UI = {}; App.UI = {};
App.UI.DOM = {}; App.UI.DOM = {};
......
:: JS Random Event [nobr]
<<run html5passage(n => $event.execute(n))>>
/** base class for class-based events */
App.Events.BaseEvent = class BaseEvent {
/** build a new event
* parameters are necessary for serialization (so that saving with the event active will work correctly) and should not normally be used directly
* child classes should forward to this implementation */
constructor(actors, params) {
/** @member {Array<number>} actors - a list of IDs for the actors participating in this event. */
this.actors = actors || [];
/** @member {object} params - a set of parameters to pass to the event. */
this.params = params || {};
}
/** generate an array of zero or more predicates which must all be true in order for the event to be valid.
* lambda predicates may add properties to {@link App.Events.BaseEvent#params the params member} in order to pass information on to the event.
* child classes should implement this.
* @returns {Array<Function>}
*/
eventPrerequisites() {
return [];
}
/** generate an array of zero or more arrays, each corresponding to an actor in the event, which contain zero or more predicates which must be satisfied by the actor.
* child classes should implement this.
* @returns {Array<Array<Function>>}
*/
actorPrerequisites() {
return [];
}
/** run the event and attach DOM output to the event passage.
* child classes must implement this.
* @param {DocumentFragment} node - Document fragment which event output should be attached to
*/
execute(node) {
}
/** clone the event (needed for serialization).
* default implementation should suffice for child classes */
clone() {
let c = {};
Reflect.setPrototypeOf(c, Reflect.getPrototypeOf(this));
deepAssign(c, this);
return c;
}
/** serialize the event instance so it persists through saves.
* default implementation should suffice for child classes assigned to App.Events */
toJSON() {
return JSON.reviveWrapper(`new App.Events.${this.constructor.name}(${JSON.stringify(this.actors)},${JSON.stringify(this.params)})`);
}
/** build the actual list of actors that will be involved in this event.
* default implementation should suffice for child classes with a fixed number of actors; may be overridden for events with variable actor count.
* @param {App.Entity.SlaveState} firstActor - if non-null, the first actor should be this slave (fail if she is not qualified)
* @returns {boolean} - return false if sufficient qualified actors could not be found (cancel the event)
*/
castActors(firstActor) {
const prereqs = this.actorPrerequisites();
let i = 0;
if (firstActor && prereqs.length > 0) {
if (prereqs[0].every(p => p(firstActor))) {
this.actors.push(firstActor.ID);
} else {
return false; // preselected first actor was not qualified
}
i = 1; // first actor is cast
}
for (; i < prereqs.length; ++i) {
const qualified = V.slaves.filter(s => !this.actors.includes(s.ID) && prereqs[i].every(p => p(s)));
if (qualified.empty) {
return false; // a required actor was not found
}
this.actors.push(qualified.pluck().ID);
}
return true; // all actors cast
}
};
/* This is a trivial event for use as an example. */
App.Events.TestEvent = class TestEvent extends App.Events.BaseEvent {
constructor(actors, params) {
super(actors, params);
}
eventPrerequisites() {
return [];
}
actorPrerequisites() {
return [
[] // one actor, no requirements
];
}
execute(node) {
let [eventSlave] = this.actors.map(a => getSlave(a)); // mapped deconstruction of actors into local slave variables
node.appendChild(document.createTextNode(`This test event for ${eventSlave.slaveName} was successful.`));
}
};
/* Note that we use a much more strict delineation between individual and nonindividual events here than in the old event system.
* Individual events always trigger for the chosen event slave, and the first actor is always the event slave.
* Nonindividual events are not provided any event slave and should cast one themselves.
*/
/** get a list of possible individual event based on a given available main actor
* @param {App.Entity.SlaveState} slave
* @returns {Array<App.Events.BaseEvent>}
*/
App.Events.getIndividualEvents = function(slave) {
return [
// instantiate all possible random individual events here
new App.Events.TestEvent()
]
.filter(e => (e.eventPrerequisites().every(p => p()) && e.castActors(slave)));
};
/** get a list of possible nonindividual events
* @returns {Array<App.Events.BaseEvent>}
*/
App.Events.getNonindividualEvents = function() {
return [
// instantiate all possible random nonindividual events here
]
.filter(e => (e.eventPrerequisites().every(p => p()) && e.castActors(null)));
};
/* --- below here is a bunch of workaround crap because we have to somehow persist event selection through multiple twine passages. ---
* eventually all this should go away, and we should use just one simple passage for both selection and execution, so everything can be kept in object form instead of being continually serialized and deserialized.
* we need to be able to serialize/deserialize the active event anyway so that saves work right, so this mechanism just piggybacks on that capability so the event passages don't need to be reworked all at once
*/
/** get a stringified list of possible individual events as fake passage names - TODO: kill me */
App.Events.getIndividualEventsPassageList = function(slave) {
const events = App.Events.getIndividualEvents(slave);
return events.map(e => `JSRE ${e.constructor.name}:${JSON.stringify(e.toJSON())}`);
};
/** get a stringified list of possible individual events as fake passage names - TODO: kill me */
App.Events.getNonindividualEventsPassageList = function() {
const events = App.Events.getNonindividualEvents();
return events.map(e => `JSRE ${e.constructor.name}:${JSON.stringify(e.toJSON())}`);
};
/** execute a fake event passage from the embedded JSON - TODO: kill me */
App.Events.setGlobalEventForPassageTransition = function(psg) {
V.event = JSON.parse(psg.slice(psg.indexOf(":") + 1));
};
/** strip the embedded JSON from the fake event passage so it can be read by a human being - TODO: kill me */
App.Events.printEventPassage = function(psg) {
return psg.slice(0, psg.indexOf(":"));
};
:: random event roll :: random event roll
<<set $goto = $events.random()>> <<set $goto = $events.random()>>
<<if $goto.startsWith("JSRE")>>
<<run App.Events.setGlobalEventForPassageTransition($goto)>>
<<goto "JS Random Event">>
<</if>>
<<goto $goto>> <<goto $goto>>
...@@ -14,11 +14,19 @@ ...@@ -14,11 +14,19 @@
''A random event would have been selected from the following:'' ''A random event would have been selected from the following:''
<<set $RESSeventIndex = 0>> <<set $RESSeventIndex = 0>>
<<for $i = 0; $i < $events.length; $i++>> <<for $i = 0; $i < $events.length; $i++>>
<br>[[$events[$i]]] <<capture $i>>
<<if $events[$i] == "RESS">> <br>
&ndash; $RESSevent[$RESSeventIndex] <<if $events[$i].startsWith("JSRE")>>
<<set $RESSeventIndex += 1>> <<set _linkText = App.Events.printEventPassage($events[$i])>>
<<link _linkText "JS Random Event">><<run App.Events.setGlobalEventForPassageTransition($events[$i])>><</link>>
<<else>>
[[$events[$i]]]
<<if $events[$i] == "RESS">>
&ndash; $RESSevent[$RESSeventIndex]
<<set $RESSeventIndex += 1>>
<</if>>
<</if>> <</if>>
<</capture>>
<</for>> <</for>>
<br><br> <br><br>
//RESS is an amalgamated Random Event, Single Slave that combines existing single slave random events// //RESS is an amalgamated Random Event, Single Slave that combines existing single slave random events//
......
...@@ -119,7 +119,7 @@ ...@@ -119,7 +119,7 @@
<</if>> <</if>>
/* EVENT RANDOMIZATION */ /* EVENT RANDOMIZATION */
<<set $events = populateEventArray()>> <<set $events = populateEventArray().concat(App.Events.getIndividualEventsPassageList($eventSlave))>>
<<if $cheatMode == 1>> <<if $cheatMode == 1>>
<<goto "random event select">> <<goto "random event select">>
......
...@@ -1468,7 +1468,7 @@ ...@@ -1468,7 +1468,7 @@
<<set _recruitEvents.shuffle()>> <<set _recruitEvents.shuffle()>>
<<set _recruitEvents.length = _maxRecruitNumber>> <<set _recruitEvents.length = _maxRecruitNumber>>
<</if>> <</if>>
<<set $events = $events.concat(_recruitEvents)>> <<set $events = $events.concat(_recruitEvents).concat(App.Events.getNonindividualEventsPassageList())>>
<<if $cheatMode == 1>> <<if $cheatMode == 1>>
<<goto "random event select">> <<goto "random event select">>
<<else>> <<else>>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment