After further reading, re-combined the interfaces

= There wasn't really any point in trying to split the interfaces all
the way up to the top level - this is a game; it is inevitable that some object data
will change and some will stay the same
This commit is contained in:
loplkc loplkc 2022-07-12 11:40:04 -04:00
parent 8a9d5414f8
commit 88129a87ba
3 changed files with 106 additions and 70 deletions

View file

@ -1,3 +1,4 @@
--An attempt to profile the speed of TweenService:GetValue
local TweenService = game:GetService("TweenService")
local clock = os.clock
local first = clock()

View file

@ -1,12 +1,19 @@
// "EntityManager": Create entity objects and apply transformations to them.
// A bold attempt at functional programming in a videogame entity system
// A bold (or just foolish) attempt at functional programming in a videogame entity system
import { makePuppet, puppet, puppetEntry } from "./Puppetmaster";
// import { ability } from "./AbilityManager";
const entityTags = ["minion"] as const
const entityTags = ["minion"] as const;
type modifiers = readonly [power: number, speed: number, defense: number, resistance: number]; // values used for calculation, only modified by buffs and debuffs
type statuses = readonly [health: number, barrier: number]; // values used to store an entity's current status (more existential than stats)
type statusEffect = readonly [string, modifiers, entityTransformTemplate, number];
type entityTag = typeof entityTags[number]
type statusEffect = readonly [
name: string,
type: string,
strength: number,
modifiers?: modifiers,
effect?: entityTransformTemplate,
]; // Should also be able to cause a visual effect somehow - maybe rather than having an entityTransformTemplate, it should broadcast a message back to the top
// ...that can affect other entities or communicate with the client
type entityTag = typeof entityTags[number];
export interface entityId {
readonly name: string;
readonly team: "players" | "enemies";
@ -15,20 +22,21 @@ export interface entityId {
};
}
export interface entityProperties {
readonly id: entityId,
readonly baseModifiers: modifiers,
readonly maxStatuses: statuses,
readonly baseStatusEffects: statusEffect[],// Permanent immunities/status effects
readonly id: entityId;
readonly maxStatuses: statuses;
readonly baseModifiers: modifiers;
readonly baseStatusEffects: statusEffect[]; // Permanent immunities/status effects
}
export interface entityStatuses {
readonly statuses: statuses; // Health and stuff that change constantly
readonly statusEffects: statusEffect[]; // Burn, poison, etc.
export interface entity {
statuses: statuses;
statusEffects: statusEffect[];
readonly properties: entityProperties;
}
type entityTransformType = "support" | "attack";
type healthTransformValue = [magnitude: number, affectsHealth: boolean, affectsBarrier: boolean]
type healthTransformValue = [magnitude: number, affectsHealth: boolean, affectsBarrier: boolean];
interface entityTransformTemplate {
healthTransformValue: healthTransformValue,
healthTransformValue: healthTransformValue;
statusEffectsGranted?: placeholder[]; // TBA (Stuff like burn, slow, stun, etc.)
}
type entityTransformDeterminer = (
@ -43,7 +51,8 @@ interface ability {
// Immunities are just status effects that take the place of a damaging status effect (so ones of that type can't be added) but don't do anything
// Status effects have a strength value that determines whether they can override another status effect of the same type (holy inferno can replace burn but burn can't replace holy inferno)
export interface entityController {//Deprecated
export interface entityController {
//Deprecated
setPosition: (location: Vector3) => void;
useAbility: (abilityName: string, activating: boolean) => void;
}
@ -124,7 +133,12 @@ function applyAttackToEntityStatuses(
): statuses {
// + Apply status effects of receiving entity to damage (e.g. armor break)
const modifiedDamage = applyModifiersToDamage(entityTransformTemplate.magnitude, entityModifiers);
const newStatuses = applyDamageToStatuses(entityStatuses, modifiedDamage, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier);
const newStatuses = applyDamageToStatuses(
entityStatuses,
modifiedDamage,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
);
return entityStatuses;
}
/*function applyHealToEntityStats() {//if (transformType === "attack") {
@ -150,7 +164,13 @@ function applyHealToEntityStatuses(
entityStatusEffects: statusEffect[],
): statuses {
// + Apply status effects of receiving entity to damage (e.g. heal up)
const newStatuses = applyHealToStatuses(entityStatuses, entityTransformTemplate.magnitude, entityTransformTemplate.affectsHealth, entityTransformTemplate.affectsBarrier, entityMaxStatuses);
const newStatuses = applyHealToStatuses(
entityStatuses,
entityTransformTemplate.magnitude,
entityTransformTemplate.affectsHealth,
entityTransformTemplate.affectsBarrier,
entityMaxStatuses,
);
return entityStatuses;
}
// ? Are you meant to pipe determineEntityTransform into the resulting function?
@ -178,7 +198,7 @@ function applyEntityToHeal(
entityTransformTemplate: entityTransformTemplate,
): entityTransformTemplate {
// + Apply user's status effects to outgoing heal
return entityTransformTemplate // There could be a heal modifier later
return entityTransformTemplate; // There could be a heal modifier later
}
/*function transformEntityStats(
entityStats: entityStats,
@ -307,17 +327,19 @@ function getEntityByName(entityList: entity[], entityName: string): success<enti
return [false, "Entity not found"];
}
function getAbility() {}
function applyEntityTransformToEntityStatuses(entityTransform: entityTransformTemplate, statuses: statuses, maxStatuses: statuses) {
}
function applyEntityTransformToEntityStatuses(
entityTransform: entityTransformTemplate,
statuses: statuses,
maxStatuses: statuses,
) {}
function applyEntityTransformToEntityList(
entityList: entity[],
entityTransformDeterminer: entityTransformDeterminer,
entityPerformingTransform: entity,
aim: Vector3,
): [entity[], placeholder[]?] {
const entityPerformingTransformModifiers = entityPerformingTransform.baseModifiers
const entityPerformingTransformStatusEffects = entityPerformingTransform.stats.statusEffects // Not good
const entityPerformingTransformModifiers = entityPerformingTransform.baseModifiers;
const entityPerformingTransformStatusEffects = entityPerformingTransform.stats.statusEffects; // Not good
const newEntityList = entityList.map(function (entityReceivingTransform: entity): entity {
const entityTransform = entityTransformDeterminer(
entityPerformingTransform.id,
@ -326,13 +348,13 @@ function applyEntityTransformToEntityList(
entityReceivingTransform.puppet,
);
if (entityTransform) {
const entityStatuses = entityPerformingTransform.stats.statuses
const [entityTransformType, entityTransformTemplate] = entityTransform
const entityStatuses = entityPerformingTransform.stats.statuses;
const [entityTransformType, entityTransformTemplate] = entityTransform;
if (entityTransformType === "attack") {
const outgoingTransformTemplate = applyEntityToAttack(
entityPerformingTransformModifiers,
entityPerformingTransformStatusEffects,
entityTransformTemplate
entityTransformTemplate,
);
const newEntityStatuses = outgoingTransformTemplate
? applyAttackToEntityStatuses(
@ -343,11 +365,12 @@ function applyEntityTransformToEntityList(
)
: entityStatuses; // Also cringe
} else {
assert(entityTransformType === "heal")
const outgoingTransformTemplate = applyEntityToHeal( // Lots of DRY violations here
assert(entityTransformType === "heal");
const outgoingTransformTemplate = applyEntityToHeal(
// Lots of DRY violations here
entityPerformingTransformModifiers,
entityPerformingTransformStatusEffects,
entityTransformTemplate
entityTransformTemplate,
);
const newEntityStatuses = outgoingTransformTemplate
? applyHealToEntityStatuses(

View file

@ -1,28 +1,37 @@
// "The": Handle events.
import { entityId, entityProperties, entityStatuses/*entityManagerRequest /*makeEntity, entityController*/ } from "./EntityManager";
import { applyRequestsToPlayerStorage, playerManagerRequest } from "./PlayerManager"
export type sceneManagerRequest = [Player, "useAbility", Vector3] | [Player, "foo", "bar"]
type endConditionFunction = (containedScenes: scene[], containedEntities: entity[], timeElapsed: number) => boolean
import { entity } from "./EntityManager";
import { applyRequestsToPlayerStorage, playerManagerRequest } from "./PlayerManager";
export type sceneManagerRequest = [Player, "useAbility", Vector3] | [Player, "foo", "bar"];
type endConditionFunction = (containedEntities: entity[], timeElapsed: number) => boolean;
export interface sceneTemplate {
readonly sceneComplete: endConditionFunction // Checks conditions that need to pass for the scene to end (e.g. entityX.Alive == false || timeSpent > 1000)
readonly onCompletion: readonly playerManagerRequest[] // Requests to get sent out when the scene ends
readonly sceneComplete: endConditionFunction; // Checks conditions that need to pass for the scene to end (e.g. entityX.Alive == false || timeSpent > 1000)
readonly onCompletion: readonly playerManagerRequest[]; // Requests to get sent out when the scene ends
}
export interface scene extends sceneTemplate {
export interface scene {
entities: entity[];
}
/*export interface sceneBackstage {
readonly entityProperties: {
[glumph: string]: entityProperties;
}
}*/
// export interface scene extends sceneTemplate {
/*containedScenes?: {
[sceneName: string]: scene | undefined
}; // Scenes within this scene that are isolated from each other and can be run in parallel
// Not necessarily "A list of events that need to return true (in sequence) to complete this event", but such events would go there
*/ //Scene nesting is currently cancelled
containedEntities: string[]; // "A list of entities that need to die to complete the event" (not really but kinda)
containedEntityProperties: {
[entityName: string]: entityProperties
}
containedEntityStatuses
players: {
[userId: number]: [inThisScene: true] | [inThisScene: false, subScene: string] | undefined
}
// containedEntities: string[]; // "A list of entities that need to die to complete the event" (not really but kinda)
// containedEntityProperties: {
// [entityName: string]: entityProperties
// }
// containedEntityStatuses
// players: {
// [userId: number]: [inThisScene: true] | [inThisScene: false, subScene: string] | undefined
// }
//timeout?: number; // A timeout for the event; passes a lose condition if there are other completion requirements that have not been satisfied
}
// }
export function runScene(scene: scene, now: number): success<[scene, playerManagerRequest[]]> {
return [true, [scene, []]];
}
@ -60,19 +69,22 @@ function applyRequestToScene(scene: scene, now: number, request: sceneManagerReq
return [scene, sceneRequestResult[1]]
}*/
}
export function applyRequestsToScene(scene: scene, now: number, requests: sceneManagerRequest[]): success<[scene, playerManagerRequest[]]> {
export function applyRequestsToScene(
scene: scene,
now: number,
requests: sceneManagerRequest[],
): success<[scene, playerManagerRequest[]]> {
try {
let newScene: scene = scene
let outgoingRequests: playerManagerRequest[] = []
let newScene: scene = scene;
let outgoingRequests: playerManagerRequest[] = [];
requests.forEach(function (request: sceneManagerRequest) {
const sceneRequestResult = applyRequestToScene(newScene, now, request);
newScene = sceneRequestResult[0]
outgoingRequests = [...outgoingRequests, ...sceneRequestResult[1]]
})
newScene = sceneRequestResult[0];
outgoingRequests = [...outgoingRequests, ...sceneRequestResult[1]];
});
return [true, [newScene, outgoingRequests]];
}
catch(error) {
return [false, error]
} catch (error) {
return [false, error];
}
}
export function initScene(sceneTemplate: sceneTemplate): scene {
@ -82,6 +94,6 @@ export function initScene(sceneTemplate: sceneTemplate): scene {
players: [],
sceneComplete: sceneTemplate.sceneComplete,
onCompletion: sceneTemplate.onCompletion,
}
return newScene;
};
return newScene;
}