Skip to content

Commit

Permalink
Add hints for props that lead to higher places
Browse files Browse the repository at this point in the history
  • Loading branch information
krypciak committed Jan 10, 2024
1 parent ca9052e commit 49ba5f3
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

180 changes: 180 additions & 0 deletions src/hint-system/climbable-terrain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { MenuOptions } from '../optionsManager'
import { HintSystem } from './hint-system'
import { ClimbableMenuSettings } from '../types'

const crypto: typeof import('crypto') = (0, eval)('require("crypto")')

export class ClimbableTerrainHints {
private createFakeEntity(pos: Vec3, size: Vec3) {
return {
coll: { pos, size },
uuid: crypto.createHash('sha256'),
getCenter: function () {
return Vec2.createC(this.coll.pos.x + this.coll.size.x / 2, this.coll.pos.y + this.coll.size.y / 2)
},
getAlignedPos: ig.Entity.prototype.getAlignedPos,
}
}

private getLevelFromZ(z: number): number {
for (const i in ig.game.levels) {
if (ig.game.levels[i].height! + 5 >= z && ig.game.levels[i].height! - 5 <= z) {
return parseInt(i)
}
}
return -1
}
private getSurroundingTiles(e: ig.Entity): Vec3[] {
const p: Vec3 = e.coll.pos
const s: Vec3 = e.coll.size

const x1 = (p.x / 16 - 1).floor()
const x2 = ((p.x + s.x) / 16 + 1).ceil()
const y1 = (p.y / 16 - 1).floor()
const y2 = ((p.y + s.y) / 16 + 1).ceil()

const lvl = this.getLevelFromZ(p.z)
if (lvl == -1) {
return []
}

const tiles: Vec3[] = []
for (let x = x1 + 1; x < x2; x++) {
tiles.push(Vec3.createC(x, y1, lvl))
tiles.push(Vec3.createC(x, y2, lvl))
}
for (let y = y1 + 1; y < y2; y++) {
tiles.push(Vec3.createC(x1, y, lvl))
tiles.push(Vec3.createC(x2, y, lvl))
}
return tiles
}

private isTileBlocking(tile: number | undefined): boolean {
if (tile === undefined) return false
return tile == 2 || (tile >= 8 && tile <= 11) || (tile >= 19 && tile <= 23)
}

private isTileJumpable(tilePos: Vec3): boolean {
const levels = Object.values(ig.game.levels).filter(l => l.collision && l.height !== undefined)
const origLvl = levels[tilePos.z]
const nextLvlI = levels.findIndex(o => o.height == tilePos.z + 32)
let nextLvl = levels[nextLvlI]
if (!nextLvl) {
return false
}
let t1
if (origLvl.collision?.data) {
t1 = origLvl.collision.data[tilePos.y][tilePos.x]
}
let t2, t4
if (nextLvl.collision?.data) {
t2 = nextLvl.collision.data[tilePos.y][tilePos.x]
t4 = (nextLvl.collision.data[tilePos.y - 2] ?? [])[tilePos.x]
}
let nextnextLvl = levels[nextLvlI + 1]
let t3
if (nextnextLvl?.collision?.data) {
t3 = nextnextLvl.collision.data[tilePos.y][tilePos.x]
}
return t1 == 2 && t2 == 1 && (t3 == undefined || t3 == 1) && (t4 == undefined || !this.isTileBlocking(t4))
}

private checkIsJumpable(e: ig.Entity): boolean {
const sur = this.getSurroundingTiles(e)
for (const tile of sur) {
if (this.isTileJumpable(tile)) {
return true
}
}
return false
}

constructor() {
/* in prestart */
const self = this

ig.Game.inject({
loadingComplete() {
this.parent()
// const levels = Object.values(ig.game.levels).filter(l => l.collision && l.height)
// console.log(levels)
/* remove distracting props */
if (MenuOptions.hints && ig.game.mapName == 'cargo-ship.teleporter') {
ig.game.shownEntities.forEach(e => e instanceof ig.ENTITY.Prop && e.propName.startsWith('cargo-box') && e.kill())
}
},
})

sc.ClimbableMenu = sc.BasicHintMenu.extend({
init(settings: ClimbableMenuSettings) {
const prop: ig.ENTITY.Prop = settings.entity as ig.ENTITY.Prop
const pt = prop.propName
let name = ''
let description = ''
if (pt.startsWith('cargo-box')) {
name = 'Cargo box, jumpable'
description = 'You can jump on me to get to higher places!'
}
this.parent(() => {
return [name, description, null]
})
},
})
sc.QUICK_MENU_TYPES.Climbable = sc.QuickMenuTypesBase.extend({
init(type: string, settings: ClimbableMenuSettings, screen: sc.QuickFocusScreen) {
const entity: ig.Entity = settings.entity ?? self.createFakeEntity(settings.pos!, settings.size ?? Vec3.createC(16, 16, 0))
settings.entity = entity
this.parent(type, settings as any, screen)
this.setIconColor(sc.ANALYSIS_COLORS.GREY)
this.showType = sc.SHOW_TYPE.INSTANT

this.nameGui = new sc.ClimbableMenu(settings)
this.nameGui.setPivot(this.nameGui.hook.size.x / 2, 0)
this.nameGui.hook.transitions = {
DEFAULT: { state: {}, time: 0.1, timeFunction: KEY_SPLINES.EASE },
HIDDEN: { state: { alpha: 0, scaleX: 0.3, offsetY: 8 }, time: 0.2, timeFunction: KEY_SPLINES.LINEAR },
}
this.nameGui.doStateTransition('HIDDEN', true)
this.screen.addSubGui(this.nameGui)
},
onAnalysisEnter() {
this.nameGui.setPosition(this.hook, this.entity)
this.parent()
},
onAnalysisExit() {
this.parent()
this.nameGui.doStateTransition('HIDDEN')
},
focusGained() {
this.parent()
this.nameGui.doStateTransition('DEFAULT')
HintSystem.g.activateHint(0, this)
},
focusLost() {
this.parent()
this.nameGui.doStateTransition('HIDDEN')
HintSystem.g.deactivateHint(0)
},
alignGuiPosition() {
this.parent()
this.nameGui.setPosition(this.hook, this.entity)
},
})

ig.ENTITY.Prop.inject({
getQuickMenuSettings(): Omit<sc.QuickMenuTypesBaseSettings, 'entity'> {
return {
type: 'Climbable',
hintName: 'ClimbableProp',
disabled: !(MenuOptions.hints && this.coll.type == ig.COLLTYPE.BLOCK && this.propName.startsWith('cargo-box') && self.checkIsJumpable(this)),
}
},
})
sc.QuickMenuAnalysis.inject({
populateHintList() {
this.parent()
},
})
}
}
6 changes: 4 additions & 2 deletions src/hint-system/hint-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SpecialAction } from '../special-action'
import { TextGather } from '../tts/gather-text'
import { AimAnalyzer, isAiming } from './aim-analyze'
import { AnalyzableHintMenu } from './analyzable-override'
import { ClimbableTerrainHints } from './climbable-terrain'
import { EnemyHintMenu } from './enemy-override'
import { HBounceBlock, HBounceSwitch } from './hints/bounce-puzzles'
import { HDestructible } from './hints/destructible'
Expand All @@ -14,7 +15,7 @@ import { HDoor, HElevator, HTeleportField, HTeleportGround } from './hints/tprs'
import { HWalls } from './hints/walls'
import { NPCHintMenu } from './npc-override'

export const HintTypes = ['All', 'NPC', 'Enemy', 'Interactable', 'Selected'] as const /* "Analyzable" category integrated into "Interactable" */
export const HintTypes = ['All', 'NPC', 'Enemy', 'Interactable', 'Climbable', 'Selected'] as const /* "Analyzable" category integrated into "Interactable" */
export const HintSubTypes = ['Puzzle', 'Plants'] as const

export interface HintData {
Expand All @@ -29,7 +30,7 @@ export interface Hint {

export type ReqHintEntry = { entity: ig.Entity; nameGui: { description: sc.TextGui; title: sc.TextGui; description2: string | null } }

export type HintUnion = sc.QUICK_MENU_TYPES.Hints | sc.QUICK_MENU_TYPES.NPC | sc.QUICK_MENU_TYPES.Enemy | sc.QUICK_MENU_TYPES.Analyzable
export type HintUnion = sc.QUICK_MENU_TYPES.Hints | sc.QUICK_MENU_TYPES.NPC | sc.QUICK_MENU_TYPES.Enemy | sc.QUICK_MENU_TYPES.Analyzable | sc.QUICK_MENU_TYPES.Climbable

export class HintSystem implements PauseListener {
static g: HintSystem
Expand Down Expand Up @@ -511,5 +512,6 @@ export class HintSystem implements PauseListener {
new NPCHintMenu()
new EnemyHintMenu()
new AnalyzableHintMenu()
new ClimbableTerrainHints()
}
}
19 changes: 19 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { HintUnion, HintSubTypes } from './hint-system/hint-system'

export {}

export type ClimbableMenuSettings = sc.QuickMenuTypesBase & {
pos?: Vec3
size?: Vec3
}

declare global {
namespace ig {
interface SoundHandleBase {
Expand Down Expand Up @@ -66,6 +71,14 @@ declare global {
interface Analyzable {
nameGui: sc.AnalyzableHintMenu
}

interface Climbable extends sc.QuickMenuTypesBase {
nameGui: sc.ClimbableMenu
}
interface ClimbableConstructor extends ImpactClass<Climbable> {
new (type: string, settings: ClimbableMenuSettings, screen: sc.QuickFocusScreen): Climbable
}
var Climbable: ClimbableConstructor
}
interface BasicHintMenu extends ig.BoxGui {
getText: () => [string, string, string | null]
Expand Down Expand Up @@ -107,6 +120,12 @@ declare global {
}
var AnalyzableHintMenu: AnalyzableHintMenuConstructor

interface ClimbableMenu extends sc.BasicHintMenu {}
interface ClimbableMenuConstructor extends ImpactClass<ClimbableMenu> {
new (settings: ClimbableMenuSettings): ClimbableMenu
}
var ClimbableMenu: ClimbableMenuConstructor

interface QuickMenuTypesBaseSettings {
hintName?: string
hintType?: (typeof HintSubTypes)[number]
Expand Down

0 comments on commit 49ba5f3

Please sign in to comment.