FILES ----- player.config spawner.config universe_server.config behaviors\npc\combat.behavior behaviors\npc\merchant.behavior behaviors\npc\react.behavior [NEW] behaviors\npc\tutorialtenant.behavior behaviors\npc\wander.behavior biomes\distributions.config biomes\surface\forest.biome biomes\surface_detached\mushroompatch.biome [NEW] dungeons\microdungeons\biomes\forest\forestmicrodungeons.dungeon [NEW] dungeons\microdungeons\biomes\forest\lake1.json dungeons\microdungeons\biomes\garden\archway.json dungeons\microdungeons\biomes\garden\archway1.json dungeons\microdungeons\biomes\garden\gardenbridge1.json dungeons\microdungeons\biomes\garden\gardenbridge2.json dungeons\microdungeons\biomes\garden\gardenhouseruin1.json dungeons\microdungeons\biomes\garden\gardenmicrodungeons.dungeon dungeons\microdungeons\biomes\garden\grave1.json dungeons\microdungeons\biomes\garden\grave2.json [NEW] dungeons\microdungeons\biomes\garden\graveyard.json [NEW] dungeons\microdungeons\biomes\garden\graveyardflowers.json dungeons\microdungeons\biomes\garden\littlecave1.json dungeons\microdungeons\biomes\garden\med1.json dungeons\microdungeons\biomes\garden\mediumburriedruin.json dungeons\microdungeons\biomes\garden\mediumgardenruin1.json dungeons\microdungeons\biomes\garden\mediumgardenruincamp.json dungeons\microdungeons\biomes\garden\mediumgardenruinplain.json dungeons\microdungeons\biomes\garden\mediumgardenruintall.json [NEW] dungeons\microdungeons\biomes\garden\mediumruin.json [NEW] dungeons\microdungeons\biomes\garden\moundmedium.json dungeons\microdungeons\biomes\garden\small1.json dungeons\microdungeons\biomes\garden\smallcamp.json dungeons\microdungeons\biomes\garden\smallgardenflowers.json [NEW] dungeons\microdungeons\biomes\garden\smallpile.json [NEW] dungeons\microdungeons\biomes\garden\smallstonehouse.json dungeons\microdungeons\biomes\garden\smalltower.json dungeons\microdungeons\biomes\garden\smalltower2.json [NEW] dungeons\microdungeons\biomes\garden\tunnelsmall1.json dungeons\microdungeons\biomes\garden\wall1.json dungeons\microdungeons\biomes\garden\wall2.json [NEW] dungeons\microdungeons\biomes\garden\wall3.json [NEW] dungeons\microdungeons\biomes\garden\well1.json [NEW] dungeons\microdungeons\biomes\garden\well2.json [NEW] dungeons\microdungeons\biomes\garden\well3.json dungeons\microdungeons\biomes\garden\xtra1.json dungeons\microdungeons\randomencounter\unsorted\shroomencounter\1.png dungeons\microdungeons\randomencounter\unsorted\shroomencounter\2.png dungeons\microdungeons\randomencounter\unsorted\shroomencounter\3.png dungeons\microdungeons\randomencounter\unsorted\shroomencounter\4.png dungeons\microdungeons\randomencounter\unsorted\shroomencounter\5.png dungeons\microdungeons\randomencounter\unsorted\shroomencounter\6.png dungeons\other\naturalcave\campsite1left.png dungeons\other\naturalcave\campsite1right.png dungeons\other\naturalcave\campsite2left.png dungeons\other\naturalcave\campsite2right.png dungeons\other\naturalcave\campsite3left.png dungeons\other\naturalcave\campsite3right.png dungeons\other\naturalcave\campsite4left.png dungeons\other\naturalcave\campsite4right.png dungeons\other\naturalcave\campsite5left.png dungeons\other\naturalcave\campsite5right.png dungeons\other\naturalcave\campsite6left.png dungeons\other\naturalcave\campsite6right.png dungeons\other\naturalcave\entrance1-objects.png dungeons\other\naturalcave\entrance2a-objects.png dungeons\other\naturalcave\entrance2b-objects.png dungeons\other\naturalcave\entrance2c-objects.png dungeons\other\naturalcave\entrance3-objects.png dungeons\other\naturalcave\entrance5-objects.png dungeons\other\naturalcave\entrance6-objects.png dungeons\other\naturalcave\naturalcave.dungeon items\armors\backerhats\mughat\mughat.head [NEW] items\materials\stonerubble.matitem [NEW] items\materials\stonerubble.png [NEW] monsters\critter\blipcritter\blipcritter.animation [NEW] monsters\critter\blipcritter\blipcritter.monstertype [NEW] monsters\critter\blipcritter\body.frames [NEW] monsters\critter\blipcritter\body.monsterpart [NEW] monsters\critter\blipcritter\body.png [NEW] monsters\critter\sporelingcritter\body.frames [NEW] monsters\critter\sporelingcritter\body.monsterpart [NEW] monsters\critter\sporelingcritter\body.png [NEW] monsters\critter\sporelingcritter\sporelingcritter.animation [NEW] monsters\critter\sporelingcritter\sporelingcritter.monstertype npcs\airshipquartermaster.npctype npcs\bmain.lua npcs\chefmerchant.npctype npcs\default_reactions.config npcs\doctormerchant.npctype npcs\merchant.npctype npcs\shroommerchant.npctype npcs\stimmerchant.npctype npcs\toolmerchant.npctype [NEW] npcs\tutorialtenant.npctype npcs\wanderingmerchant.npctype npcs\wizardmerchant.npctype objects\colony\colonydeed\colonydeed.lua objects\colony\colonydeed\colonydeed.object objects\colony\colonydeed\scanning.lua objects\generic\travellersbeacon\travellersbeacon.object parallax\images\forestback\base\1.png [NEW] parallax\images\forestcanopy\base\1.png parallax\images\forestfront\base\1.png [NEW] parallax\images\forestlargetree\base\1.png [NEW] parallax\images\forestsmalltree\base\1.png [NEW] parallax\images\foresttreesback\base\1.png [NEW] parallax\images\foresttreeshadows\base\1.png [NEW] parallax\images\godrays\base\1.png parallax\surface\forest.parallax scripts\behavior.lua [NEW] scripts\oldpathing.lua scripts\pathing.lua scripts\util.lua scripts\actions\movement.lua scripts\actions\notification.lua scripts\actions\time.lua stagehands\coordinator.lua stagehands\coordinator\npccombat.lua stats\effects\npcreactions\nosebleed.animation stats\effects\npcreactions\vomit.animation [NEW] tenants\traveller.tenant tenants\villager_human2.tenant [NEW] tiles\materials\stonerubble.material [NEW] tiles\materials\stonerubble.png DIFFS ----- player.config 480,483d479 < "fallDamageFactor" : 2, < "minimumFallTime" : 1.0, < "minimumFallSpeed" : -2.0, < spawner.config 1028c1028,1056 < }, --- > }, > > "blipcritter" : { > "spawnParameters" : { > "area" : "surface", > "region" : "exposed", > "time" : "all" > }, > > "targetDensity" : 0.02, > "monsterType" : "blipcritter", > "monsterParameters" : { > "aggressive" : false > } > }, > > "sporelingcritter" : { > "spawnParameters" : { > "area" : "surface", > "region" : "exposed", > "time" : "all" > }, > > "targetDensity" : 0.02, > "monsterType" : "sporelingcritter", > "monsterParameters" : { > "aggressive" : false > } > }, universe_server.config 36c36 < "terrestrialBiome" : "garden", --- > "terrestrialBiome" : "forest", behaviors\npc\combat.behavior 7,8c7,8 < "/scripts/actions/movement.lua", < "/scripts/actions/position.lua" --- > "/scripts/actions/position.lua", > "/scripts/actions/movement.lua" 127c127 < "title": "dynamic", --- > "title": "parallel", 129,130c129,133 < "name": "dynamic", < "parameters": {}, --- > "name": "parallel", > "parameters": { > "fail": -1, > "success": -1 > }, 133,135c136,138 < "title": "failer", < "type": "decorator", < "name": "failer", --- > "title": "selector", > "type": "composite", > "name": "selector", 137,170c140,169 < "child": { < "title": "dynamic", < "type": "composite", < "name": "dynamic", < "parameters": {}, < "children": [ < { < "title": "sequence", < "type": "composite", < "name": "sequence", < "parameters": { < "type": "sliding", < "state": "off", < "fromEntity": "attackTarget", < "toEntity": "fleeTarget" < }, < "children": [ < { < "title": "entityInSight", < "type": "action", < "name": "entityInSight", < "parameters": { < "entity": "target" < } < }, < { < "title": "entityInRange", < "type": "action", < "name": "entityInRange", < "parameters": { < "entity": "target", < "range": 20, < "position": "self" < } --- > "children": [ > { > "title": "groupResource", > "type": "action", > "name": "groupResource", > "parameters": { > "groupId": "combat", > "name": "movePosition" > }, > "output": { > "position": "movePosition" > } > }, > { > "title": "sequence", > "type": "composite", > "name": "sequence", > "parameters": { > "type": "sliding", > "state": "off", > "fromEntity": "attackTarget", > "toEntity": "fleeTarget" > }, > "children": [ > { > "title": "entityPosition", > "type": "action", > "name": "entityPosition", > "parameters": { > "entity": "target" 172,176c171,172 < { < "title": "onGround", < "type": "action", < "name": "onGround", < "parameters": {} --- > "output": { > "position": "" 178,186d173 < ] < }, < { < "title": "parallel", < "type": "composite", < "name": "parallel", < "parameters": { < "fail": -1, < "success": -1 188,215c175,183 < "children": [ < { < "title": "entityPosition", < "type": "action", < "name": "entityPosition", < "parameters": { < "entity": "target" < }, < "output": { < "position": "targetPosition" < } < }, < { < "title": "moveToPosition", < "type": "action", < "name": "moveToPosition", < "parameters": { < "avoidLiquid": true, < "groundPosition": true, < "maxGround": 5, < "minGround": -10, < "position": "targetPosition", < "run": true, < "failFast": false < }, < "output": { < "direction": "direction" < } --- > { > "title": "groundPosition", > "type": "action", > "name": "groundPosition", > "parameters": { > "avoidLiquid": true, > "maxHeight": 5, > "minHeight": -25, > "position": "movePosition" 217,223c185,186 < { < "title": "faceDirection", < "type": "action", < "name": "faceDirection", < "parameters": { < "direction": "direction" < } --- > "output": { > "position": "" 225,227c188,207 < ] < } < ] --- > } > ] > } > ] > }, > { > "title": "moveToPosition", > "type": "action", > "name": "moveToPosition", > "parameters": { > "avoidLiquid": true, > "groundPosition": false, > "maxGround": 5, > "minGround": -10, > "position": "movePosition", > "run": true, > "failFast": false > }, > "output": { > "direction": "direction" 231,233c211,213 < "title": "failer", < "type": "decorator", < "name": "failer", --- > "title": "dynamic", > "type": "composite", > "name": "dynamic", 235,257c215,233 < "child": { < "title": "cleanup", < "type": "composite", < "name": "cleanup", < "parameters": {}, < "children": [ < { < "title": "parallel", < "type": "composite", < "name": "parallel", < "parameters": { < "fail": -1, < "success": -1 < }, < "children": [ < { < "title": "beginPrimaryFire", < "type": "action", < "name": "beginPrimaryFire", < "parameters": {} < }, < { < "title": "entityPosition", --- > "children": [ > { > "title": "sequence", > "type": "composite", > "name": "sequence", > "parameters": { > "type": "sliding", > "state": "off", > "fromEntity": "attackTarget", > "toEntity": "fleeTarget" > }, > "children": [ > { > "title": "inverter", > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "title": "inRange", 259c235 < "name": "entityPosition", --- > "name": "inRange", 261c237,275 < "entity": "target" --- > "position": "self", > "target": "movePosition", > "range": 7 > } > } > }, > { > "title": "entityInRange", > "type": "action", > "name": "entityInRange", > "parameters": { > "entity": "target", > "xRange": 25, > "position": "self" > } > }, > { > "title": "faceDirection", > "type": "action", > "name": "faceDirection", > "parameters": { > "direction": "direction" > } > }, > { > "title": "dynamic", > "type": "composite", > "name": "dynamic", > "parameters": {}, > "children": [ > { > "title": "inRange", > "type": "action", > "name": "inRange", > "parameters": { > "position": "self", > "target": "movePosition", > "range": 5 > } 263,264c277,281 < "output": { < "position": "targetPosition" --- > { > "title": "runner", > "type": "action", > "name": "runner", > "parameters": {} 265a283,299 > ] > } > ] > }, > { > "title": "cleanup", > "type": "composite", > "name": "cleanup", > "parameters": {}, > "children": [ > { > "title": "parallel", > "type": "composite", > "name": "parallel", > "parameters": { > "fail": 1, > "success": -1 267,276c301,353 < { < "title": "setAimPosition", < "type": "action", < "name": "setAimPosition", < "parameters": { < "offset": [ < 0, < 0 < ], < "position": "targetPosition" --- > "children": [ > { > "title": "entityInRange", > "type": "action", > "name": "entityInRange", > "parameters": { > "entity": "target", > "xRange": 30, > "position": "self" > } > }, > { > "title": "entityInSight", > "type": "action", > "name": "entityInSight", > "parameters": { > "entity": "target" > } > }, > { > "title": "beginPrimaryFire", > "type": "action", > "name": "beginPrimaryFire", > "parameters": {} > }, > { > "title": "entityPosition", > "type": "action", > "name": "entityPosition", > "parameters": { > "entity": "target" > }, > "output": { > "position": "targetPosition" > } > }, > { > "title": "setAimPosition", > "type": "action", > "name": "setAimPosition", > "parameters": { > "offset": [ > 0, > 0 > ], > "position": "targetPosition" > } > }, > { > "title": "runner", > "type": "action", > "name": "runner", > "parameters": {} 278,291c355,370 < }, < { < "title": "runner", < "type": "action", < "name": "runner", < "parameters": {} < } < ] < }, < { < "title": "endPrimaryFire", < "type": "action", < "name": "endPrimaryFire", < "parameters": {} --- > ] > }, > { > "title": "endPrimaryFire", > "type": "action", > "name": "endPrimaryFire", > "parameters": {} > } > ] > }, > { > "title": "faceDirection", > "type": "action", > "name": "faceDirection", > "parameters": { > "direction": "direction" 293,294c372,373 < ] < } --- > } > ] behaviors\npc\merchant.behavior 4a5,6 > "/scripts/behavior.lua", > "/scripts/actions/math.lua", 13c15 < "title": "parallel", --- > "title": "sequence", 15,19c17,18 < "name": "parallel", < "parameters": { < "fail": -1, < "success": -1 < }, --- > "name": "sequence", > "parameters": {}, 22,43c21,30 < "title": "sequence", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "title": "inRange", < "type": "action", < "name": "inRange", < "parameters": { < "position": "self", < "target": "spawn", < "range": 8 < } < }, < { < "title": "enableTrading", < "type": "action", < "name": "enableTrading", < "parameters": {} < } < ] --- > "title": "entityConfigParameter", > "type": "action", > "name": "entityConfigParameter", > "parameters": { > "default": -1, > "path": "merchant.storeRadius" > }, > "output": { > "number": "storeRadius" > } 46c33,45 < "title": "Villager", --- > "title": "entityConfigParameter", > "type": "action", > "name": "entityConfigParameter", > "parameters": { > "default": 40, > "path": "merchant.waitTime" > }, > "output": { > "number": "waitTime" > } > }, > { > "title": "parallel", 48,49c47,51 < "name": "dynamic", < "parameters": {}, --- > "name": "parallel", > "parameters": { > "fail": -1, > "success": -1 > }, 52,62d53 < "type": "module", < "path": "/behaviors/npc/tenant.behavior", < "title": "Tenant Baseline Behavior", < "parameters": {} < }, < { < "type": "module", < "path": "/behaviors/npc/flee.behavior", < "parameters": {} < }, < { 69c60,93 < "title": "wasInteracted", --- > "title": "selector", > "type": "composite", > "name": "selector", > "parameters": {}, > "children": [ > { > "title": "inverter", > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "title": "greaterThan", > "type": "action", > "name": "greaterThan", > "parameters": { > "first": "storeRadius", > "second": 0 > } > } > }, > { > "title": "inRange", > "type": "action", > "name": "inRange", > "parameters": { > "position": "self", > "target": "spawn", > "range": "storeRadius" > } > } > ] > }, > { > "title": "enableTrading", 71c95 < "name": "wasInteracted", --- > "name": "enableTrading", 73c97,105 < }, --- > } > ] > }, > { > "title": "Villager", > "type": "composite", > "name": "dynamic", > "parameters": {}, > "children": [ 75,84c107,110 < "title": "succeeder", < "type": "decorator", < "name": "succeeder", < "parameters": {}, < "child": { < "title": "resetLounging", < "type": "action", < "name": "resetLounging", < "parameters": {} < } --- > "type": "module", > "path": "/behaviors/npc/tenant.behavior", > "title": "Tenant Baseline Behavior", > "parameters": {} 87,91c113,115 < "type": "action", < "name": "faceEntity", < "parameters": { < "entity": "interactionSource" < } --- > "type": "module", > "path": "/behaviors/npc/flee.behavior", > "parameters": {} 94c118 < "title": "selector", --- > "title": "sequence", 96c120 < "name": "selector", --- > "name": "sequence", 100,102c124,132 < "title": "sequence", < "type": "composite", < "name": "sequence", --- > "title": "wasInteracted", > "type": "action", > "name": "wasInteracted", > "parameters": {} > }, > { > "title": "succeeder", > "type": "decorator", > "name": "succeeder", 104,123c134,139 < "children": [ < { < "title": "inRange", < "type": "action", < "name": "inRange", < "parameters": { < "position": "self", < "target": "spawn", < "range": 8 < } < }, < { < "title": "timer", < "type": "action", < "name": "timer", < "parameters": { < "time": 40 < } < } < ] --- > "child": { > "title": "resetLounging", > "type": "action", > "name": "resetLounging", > "parameters": {} > } 126c142,149 < "title": "sequence", --- > "type": "action", > "name": "faceEntity", > "parameters": { > "entity": "interactionSource" > } > }, > { > "title": "selector", 128c151 < "name": "sequence", --- > "name": "selector", 132,154c155,157 < "title": "sayToEntity", < "type": "action", < "name": "sayToEntity", < "parameters": { < "dialogType": "returnToStore.dialog.follow", < "entity": "interactionSource" < } < }, < { < "title": "timer", < "type": "action", < "name": "timer", < "parameters": { < "time": [ < 1, < 2 < ] < } < }, < { < "title": "succeeder", < "type": "decorator", < "name": "succeeder", --- > "title": "sequence", > "type": "composite", > "name": "sequence", 156,179c159,179 < "child": { < "title": "selector", < "type": "composite", < "name": "selector", < "parameters": {}, < "children": [ < { < "type": "module", < "path": "/behaviors/tenant/returnhome.behavior", < "title": "/behaviors/tenant/returnhome.behavior", < "parameters": {} < }, < { < "title": "moveToPosition", < "type": "action", < "name": "moveToPosition", < "parameters": { < "avoidLiquid": true, < "groundPosition": true, < "maxGround": 5, < "minGround": -5, < "position": "spawn", < "run": false, < "failFast": false --- > "children": [ > { > "title": "selector", > "type": "composite", > "name": "selector", > "parameters": {}, > "children": [ > { > "title": "inverter", > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "title": "greaterThan", > "type": "action", > "name": "greaterThan", > "parameters": { > "first": "storeRadius", > "second": 0 > } > } 181,182c181,189 < "output": { < "direction": "direction" --- > { > "title": "inRange", > "type": "action", > "name": "inRange", > "parameters": { > "position": "self", > "target": "spawn", > "range": "storeRadius" > } 183a191,198 > ] > }, > { > "title": "timer", > "type": "action", > "name": "timer", > "parameters": { > "time": "waitTime" 185,186c200,201 < ] < } --- > } > ] 189c204 < "title": "parallel", --- > "title": "sequence", 191,195c206,207 < "name": "parallel", < "parameters": { < "fail": 1, < "success": -1 < }, --- > "name": "sequence", > "parameters": {}, 198c210,219 < "title": "enableTrading", --- > "title": "sayToEntity", > "type": "action", > "name": "sayToEntity", > "parameters": { > "dialogType": "returnToStore.dialog.follow", > "entity": "interactionSource" > } > }, > { > "title": "timer", 200,201c221,227 < "name": "enableTrading", < "parameters": {} --- > "name": "timer", > "parameters": { > "time": [ > 1, > 2 > ] > } 204c230 < "title": "inverter", --- > "title": "succeeder", 206c232 < "name": "inverter", --- > "name": "succeeder", 209,214c235,263 < "title": "timer", < "type": "action", < "name": "timer", < "parameters": { < "time": 40 < } --- > "title": "selector", > "type": "composite", > "name": "selector", > "parameters": {}, > "children": [ > { > "type": "module", > "path": "/behaviors/tenant/returnhome.behavior", > "title": "/behaviors/tenant/returnhome.behavior", > "parameters": {} > }, > { > "title": "moveToPosition", > "type": "action", > "name": "moveToPosition", > "parameters": { > "avoidLiquid": true, > "groundPosition": true, > "maxGround": 5, > "minGround": -5, > "position": "spawn", > "run": false, > "failFast": false > }, > "output": { > "direction": "direction" > } > } > ] 215a265,295 > }, > { > "title": "parallel", > "type": "composite", > "name": "parallel", > "parameters": { > "fail": 1, > "success": -1 > }, > "children": [ > { > "title": "enableTrading", > "type": "action", > "name": "enableTrading", > "parameters": {} > }, > { > "title": "inverter", > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "title": "timer", > "type": "action", > "name": "timer", > "parameters": { > "time": "waitTime" > } > } > } > ] 221a302,307 > }, > { > "type": "module", > "path": "/behaviors/npc/friendlyidle.behavior", > "title": "/behaviors/npc/friendlyidle.behavior", > "parameters": {} 224,229d309 < }, < { < "type": "module", < "path": "/behaviors/npc/friendlyidle.behavior", < "title": "/behaviors/npc/friendlyidle.behavior", < "parameters": {} behaviors\npc\react.behavior 513a514,521 > "title": "timer", > "type": "action", > "name": "timer", > "parameters": { > "time": 1 > } > }, > { 552c560 < "duration": "1" --- > "duration": 0.2 563c571 < "duration": "1" --- > "duration": 0.2 593a602,609 > "title": "timer", > "type": "action", > "name": "timer", > "parameters": { > "time": 1 > } > }, > { 633c649 < "duration": "1" --- > "duration": 0.2 644c660 < "duration": "1" --- > "duration": 0.2 behaviors\npc\wander.behavior 2,335c2,335 < "name": "wander", < "description": "", < "scripts": [ < "/scripts/actions/position.lua", < "/scripts/actions/time.lua", < "/scripts/actions/movement.lua" < ], < "parameters": {}, < "root": { < "title": "Wander", < "type": "composite", < "name": "selector", < "parameters": {}, < "children": [ < { < "title": "Go inside", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "type": "decorator", < "name": "inverter", < "parameters": {}, < "child": { < "title": "isInside", < "type": "action", < "name": "isInside", < "parameters": { < "position": "self" < } < } < }, < { < "type": "action", < "name": "withinTimeRange", < "parameters": { < "range": [ < 0.5, < 0.2 < ] < } < }, < { < "title": "", < "type": "action", < "name": "findOuterDoor", < "parameters": { < "position": "self", < "range": 50 < }, < "output": { < "insidePosition": "insideDoor" < } < }, < { < "title": "", < "type": "composite", < "name": "parallel", < "parameters": { < "fail": 1, < "success": -1 < }, < "children": [ < { < "title": "", < "type": "action", < "name": "openDoors", < "parameters": { < "direction": "facing", < "distance": 1.5 < } < }, < { < "title": "", < "type": "action", < "name": "moveToPosition", < "parameters": { < "position": "insideDoor" < }, < "output": { < "direction": "direction" < } < } < ] < }, < { < "type": "decorator", < "name": "succeeder", < "parameters": {}, < "child": { < "title": "", < "type": "action", < "name": "closeDoors", < "parameters": { < "distance": 3 < } < } < } < ] < }, < { < "title": "Go outside", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "title": "isInside", < "type": "action", < "name": "isInside", < "parameters": { < "position": "self" < } < }, < { < "title": "", < "type": "action", < "name": "withinTimeRange", < "parameters": { < "range": [ < 0.2, < 0.5 < ] < } < }, < { < "title": "", < "type": "action", < "name": "findOuterDoor", < "parameters": { < "position": "self", < "range": 50 < }, < "output": { < "outsidePosition": "outsideDoor" < } < }, < { < "title": "", < "type": "composite", < "name": "parallel", < "parameters": { < "fail": 1, < "success": -1 < }, < "children": [ < { < "title": "", < "type": "action", < "name": "openDoors", < "parameters": { < "direction": "facing", < "distance": 1.5 < } < }, < { < "type": "action", < "name": "moveToPosition", < "parameters": { < "position": "outsideDoor" < }, < "output": { < "direction": "direction" < } < } < ] < }, < { < "type": "decorator", < "name": "succeeder", < "parameters": {}, < "child": { < "type": "action", < "name": "closeDoors", < "parameters": { < "distance": 3 < } < } < } < ] < }, < { < "title": "Wander but stay indoors/outdoors", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "type": "action", < "name": "setDirection", < "parameters": {}, < "output": { < "direction": "direction" < } < }, < { < "title": "Set start", < "type": "action", < "name": "offsetPosition", < "parameters": {}, < "output": { < "position": "start" < } < }, < { < "title": "faceDirection", < "type": "action", < "name": "faceDirection", < "parameters": { < "direction": "direction" < } < }, < { < "title": "", < "type": "composite", < "name": "parallel", < "parameters": { < "fail": 1, < "success": 3 < }, < "children": [ < { < "title": "Stay inside/outside", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "title": "", < "type": "action", < "name": "offsetDirection", < "parameters": { < "direction": "direction", < "multiplier": 2 < }, < "output": { < "position": "ahead" < } < }, < { < "type": "composite", < "name": "selector", < "parameters": {}, < "children": [ < { < "title": "Both inside", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "type": "action", < "name": "isInside", < "parameters": { < "position": "start" < } < }, < { < "type": "action", < "name": "isInside", < "parameters": { < "position": "ahead" < } < } < ] < }, < { < "title": "Both outside", < "type": "composite", < "name": "sequence", < "parameters": {}, < "children": [ < { < "type": "decorator", < "name": "inverter", < "parameters": {}, < "child": { < "type": "action", < "name": "isInside", < "parameters": { < "position": "start" < } < } < }, < { < "type": "decorator", < "name": "inverter", < "parameters": {}, < "child": { < "type": "action", < "name": "isInside", < "parameters": { < "position": "ahead" < } < } < } < ] < } < ] < } < ] < }, < { < "title": "timer", < "type": "action", < "name": "timer", < "parameters": { < "time": 4 < } < }, < { < "title": "", < "type": "action", < "name": "move", < "parameters": { < "direction": "direction" < } < }, < { < "title": "", < "type": "action", < "name": "openDoors", < "parameters": { < "direction": "facing", < "distance": 1.5 < } < } < ] < } < ] < } < ] < } --- > "name": "wander", > "description": "", > "scripts": [ > "/scripts/actions/position.lua", > "/scripts/actions/time.lua", > "/scripts/actions/movement.lua" > ], > "parameters": {}, > "root": { > "title": "Wander", > "type": "composite", > "name": "selector", > "parameters": {}, > "children": [ > { > "title": "Go inside", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "title": "isInside", > "type": "action", > "name": "isInside", > "parameters": { > "position": "self" > } > } > }, > { > "type": "action", > "name": "withinTimeRange", > "parameters": { > "range": [ > 0.5, > 0.2 > ] > } > }, > { > "title": "", > "type": "action", > "name": "findOuterDoor", > "parameters": { > "position": "self", > "range": 50 > }, > "output": { > "insidePosition": "insideDoor" > } > }, > { > "title": "", > "type": "composite", > "name": "parallel", > "parameters": { > "fail": 1, > "success": -1 > }, > "children": [ > { > "title": "", > "type": "action", > "name": "openDoors", > "parameters": { > "direction": "facing", > "distance": 1.5 > } > }, > { > "title": "", > "type": "action", > "name": "moveToPosition", > "parameters": { > "position": "insideDoor" > }, > "output": { > "direction": "direction" > } > } > ] > }, > { > "type": "decorator", > "name": "succeeder", > "parameters": {}, > "child": { > "title": "", > "type": "action", > "name": "closeDoors", > "parameters": { > "distance": 3 > } > } > } > ] > }, > { > "title": "Go outside", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "title": "isInside", > "type": "action", > "name": "isInside", > "parameters": { > "position": "self" > } > }, > { > "title": "", > "type": "action", > "name": "withinTimeRange", > "parameters": { > "range": [ > 0.2, > 0.5 > ] > } > }, > { > "title": "", > "type": "action", > "name": "findOuterDoor", > "parameters": { > "position": "self", > "range": 50 > }, > "output": { > "outsidePosition": "outsideDoor" > } > }, > { > "title": "", > "type": "composite", > "name": "parallel", > "parameters": { > "fail": 1, > "success": -1 > }, > "children": [ > { > "title": "", > "type": "action", > "name": "openDoors", > "parameters": { > "direction": "facing", > "distance": 1.5 > } > }, > { > "type": "action", > "name": "moveToPosition", > "parameters": { > "position": "outsideDoor" > }, > "output": { > "direction": "direction" > } > } > ] > }, > { > "type": "decorator", > "name": "succeeder", > "parameters": {}, > "child": { > "type": "action", > "name": "closeDoors", > "parameters": { > "distance": 3 > } > } > } > ] > }, > { > "title": "Wander but stay indoors/outdoors", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "type": "action", > "name": "setDirection", > "parameters": {}, > "output": { > "direction": "direction" > } > }, > { > "title": "Set start", > "type": "action", > "name": "offsetPosition", > "parameters": {}, > "output": { > "position": "start" > } > }, > { > "title": "faceDirection", > "type": "action", > "name": "faceDirection", > "parameters": { > "direction": "direction" > } > }, > { > "title": "", > "type": "composite", > "name": "parallel", > "parameters": { > "fail": 1, > "success": 3 > }, > "children": [ > { > "title": "Stay inside/outside", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "title": "", > "type": "action", > "name": "offsetDirection", > "parameters": { > "direction": "direction", > "multiplier": 2 > }, > "output": { > "position": "ahead" > } > }, > { > "type": "composite", > "name": "selector", > "parameters": {}, > "children": [ > { > "title": "Both inside", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "type": "action", > "name": "isInside", > "parameters": { > "position": "start" > } > }, > { > "type": "action", > "name": "isInside", > "parameters": { > "position": "ahead" > } > } > ] > }, > { > "title": "Both outside", > "type": "composite", > "name": "sequence", > "parameters": {}, > "children": [ > { > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "type": "action", > "name": "isInside", > "parameters": { > "position": "start" > } > } > }, > { > "type": "decorator", > "name": "inverter", > "parameters": {}, > "child": { > "type": "action", > "name": "isInside", > "parameters": { > "position": "ahead" > } > } > } > ] > } > ] > } > ] > }, > { > "title": "timer", > "type": "action", > "name": "timer", > "parameters": { > "time": 4 > } > }, > { > "title": "", > "type": "action", > "name": "move", > "parameters": { > "direction": "direction" > } > }, > { > "title": "", > "type": "action", > "name": "openDoors", > "parameters": { > "direction": "facing", > "distance": 1.5 > } > } > ] > } > ] > } > ] > } biomes\distributions.config 371c371 < "blockProbability" : 0.03 --- > "blockProbability" : 0.04 biomes\surface\forest.biome 8c8 < "extraSpawns" : [ "greentip", "blueback", "birdcritter", "squirrelcritter", "snail", "mousecritter", "owlcritter" ], --- > "extraSpawns" : [ "greentip", "blueback", "birdcritter", "squirrelcritter", "snail", "sporelingcritter", "owlcritter", "blipcritter" ], 15c15 < "extraSpawns" : [ "blueback", "redwing", "birdcritter", "squirrelcritter", "snail", "mousecritter", "owlcritter" ], --- > "extraSpawns" : [ "blueback", "redwing", "birdcritter", "squirrelcritter", "snail", "sporelingcritter", "owlcritter", "blipcritter" ], 41c41 < "dayLightColor" : [200, 200, 200], --- > "dayLightColor" : [150, 150, 150], 55c55 < "dayLightColor" : [200, 200, 200], --- > "dayLightColor" : [150, 150, 150], 69c69 < "dayLightColor" : [200, 200, 200], --- > "dayLightColor" : [150, 150, 150], 83c83 < "dayLightColor" : [200, 200, 200], --- > "dayLightColor" : [150, 150, 150], 97c97 < "dayLightColor" : [200, 200, 200], --- > "dayLightColor" : [150, 150, 150], 205a206,218 > > // MICRO DUNGEONS > > { > "mode" : "floor", > "priority" : 1, > "variants" : 1, > "distribution" : "/biomes/distributions.config:mainBiomeMicrodungeon", > > "type" : "microdungeon", > "microdungeons" : [ "forestmicrodungeons" ] > }, > biomes\surface_detached\mushroompatch.biome 8c8 < "extraSpawns" : [ ], --- > "extraSpawns" : [ "sporelingcritter" ], 15c15 < "extraSpawns" : [ ], --- > "extraSpawns" : [ "sporelingcritter" ], dungeons\microdungeons\biomes\garden\archway.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\archway1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\gardenbridge1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\gardenbridge2.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\gardenhouseruin1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\gardenmicrodungeons.dungeon 7c7,8 < "anchor" : [ "gardenbridge1", "gardenbridge2", "gardenhouseruin1", "grave1", "grave2", "littlecave1", "mediumburriedruin", "mediumgardenflowers", "mediumgardenruin1", "mediumgardenruincamp", "mediumgardenruinplain", "mediumgardenruintall", "smallgardenflowers" , "smalltower", "smalltower2" ], --- > "anchor" : [ "archway", "archway1", "gardenbridge1", "gardenbridge2", "gardenhouseruin1", "grave1", "grave2", "graveyard", "graveyardflowers", "littlecave1", "mediumburriedruin", "mediumgardenflowers", "mediumgardenruin1", "mediumgardenruincamp", "mediumgardenruinplain", "mediumgardenruintall", "mediumruin", "moundmedium", "smallcamp", "smallgardenflowers", "smallpile", "smallstonehouse", "smalltower", "smalltower2", "tunnelsmall1", "wall1", "wall2", "wall3", "well1", "well2", "well3" ], > 15a17,30 > "name" : "archway", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "archway.json" ] > }, > { > "name" : "archway1", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "archway1.json" ] > }, > { 50a66,79 > "name" : "graveyard", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "graveyard.json" ] > }, > { > "name" : "graveyardflowers", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "graveyardflowers.json" ] > }, > { 99a129,149 > "name" : "mediumruin", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "mediumruin.json" ] > }, > { > "name" : "moundmedium", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "moundmedium.json" ] > }, > { > "name" : "smallcamp", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "smallcamp.json" ] > }, > { 106a157,170 > "name" : "smallpile", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "smallpile.json" ] > }, > { > "name" : "smallstonehouse", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "smallstonehouse.json" ] > }, > { 118a183,231 > }, > { > "name" : "tunnelsmall1", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "tunnelsmall1.json" ] > }, > { > "name" : "wall1", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "wall1.json" ] > }, > { > "name" : "wall2", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "wall2.json" ] > }, > { > "name" : "wall3", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "wall3.json" ] > }, > { > "name" : "well1", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "well1.json" ] > }, > { > "name" : "well2", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "well2.json" ] > }, > { > "name" : "well3", > "rules" : [ > [ "maxSpawnCount", [1] ] > ], > "def" : [ "tmx", "well3.json" ] dungeons\microdungeons\biomes\garden\grave1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\grave2.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\littlecave1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\med1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\mediumburriedruin.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\mediumgardenruin1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\mediumgardenruincamp.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\mediumgardenruinplain.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\mediumgardenruintall.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\small1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\smallcamp.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\smallgardenflowers.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\smalltower.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\smalltower2.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\wall1.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\wall2.json [TMX file differences are left out for huge size.] dungeons\microdungeons\biomes\garden\xtra1.json [TMX file differences are left out for huge size.] dungeons\other\naturalcave\naturalcave.dungeon 516c516 < "value" : [80, 149, 91, 255], --- > "value" : [190, 255, 20, 255], items\armors\backerhats\mughat\mughat.head 8c8 < "description" : "A mug hat filled with something myseterious!", --- > "description" : "A mug hat filled with something mysterious!", npcs\airshipquartermaster.npctype 11,32d10 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua", < "/npcs/merchant/returnToStoreState.lua" < ], < npcs\bmain.lua 17a18 > self.moved = false 41a43,47 > > if not self.moved then > script.setUpdateDelta(5) > end > self.moved = false 225a232,233 > local toPosition = world.distance(position, mcontroller.position()) > mcontroller.controlFace(util.toDirection(toPosition[1])) 315c323 < end \ No newline at end of file --- > end npcs\chefmerchant.npctype 11,34d10 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/tenant.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/returnHomeState.lua", < "/npcs/grumbleState.lua", < "/npcs/chatState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\default_reactions.config 34d33 < [1.0, "nosebleed"], 39d37 < [1.0, "nosebleed"], 44c42 < [4.0, "vomit"], --- > [1.0, "vomit"], 79,80c77,78 < [1.0, "nosebleed"], < [1.0, "vomit"], --- > [0.3, "nosebleed"], > [0.3, "vomit"], npcs\doctormerchant.npctype 11,31d10 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\merchant.npctype 10,30c10 < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/tenant.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/returnHomeState.lua", < "/npcs/grumbleState.lua", < "/npcs/chatState.lua", < "/npcs/fleeState.lua", < "/npcs/sitState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/merchant/merchantState.lua", < "/npcs/merchant/returnToStoreState.lua" --- > "/npcs/bmain.lua" 33a14,16 > "behavior" : "/behaviors/npc/merchant.behavior", > "personality" : "normal", > npcs\shroommerchant.npctype 10,29d9 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\stimmerchant.npctype 10,30d9 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\toolmerchant.npctype 10,30d9 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/sensors.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\wanderingmerchant.npctype 11,31d10 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sitState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < npcs\wizardmerchant.npctype 12,32d11 < "scripts" : [ < "/npcs/main.lua", < "/scripts/pathing.lua", < "/npcs/timers.lua", < "/scripts/stateMachine.lua", < "/scripts/actions/merchant.lua", < "/scripts/util.lua", < "/scripts/vec2.lua", < < "/npcs/chatState.lua", < "/npcs/sitState.lua", < "/npcs/sleepState.lua", < "/npcs/standingIdleState.lua", < "/npcs/wanderState.lua", < < "/npcs/meleeAttackState.lua", < "/npcs/rangedAttackState.lua", < < "/npcs/merchant/merchantState.lua" < ], < objects\colony\colonydeed\colonydeed.lua 369a370 > storage.house.boundary = poly objects\colony\colonydeed\colonydeed.object 40c40 < "debug" : true, --- > "debug" : false, objects\colony\colonydeed\scanning.lua 194a195,201 > function dirDiff(d1, d2) > -- Return the difference of d1 and d2, taking into account the wrapping from > -- the end of scanDirections to the beginning. > local diff = math.abs(d1 - d2) % #scanDirections > return math.min(diff, #scanDirections - diff) > end > 230c237 < if vecEquals(position, start) and dir == startDir then --- > if vecEquals(position, start) and dirDiff(dir, startDir) <= 1 then 236a244 > util.debugPoly(poly, "red") objects\generic\travellersbeacon\travellersbeacon.object 12c12 < "apexDescription" : "A rough device, built for purpose of recording geological statistics.", --- > "apexDescription" : "A crudely made device which records geological statistics.", parallax\surface\forest.parallax 20d19 < 23,24c22,23 < "offset" : [0, 500], < "parallax" : 2 --- > "offset" : [0, 390], > "parallax" : 1.09 25a25 > 27,29c27,40 < "kind" : "forestfront", < "offset" : [0, 400], < "parallax" : 2 --- > "kind" : "forestcanopy", > "offset" : [0, -175], > "parallax" : 1.1 > }, > { > "kind" : "forestlargetree", > "offset" : [0, 90], > "parallax" : 1.11 > }, > { > "kind" : "forestcanopy", > "offset" : [0, -175], > "parallax" : 1.21, > "directives" : "?saturation=-30" 32,36c43,46 < "kind" : "foliage/treefront", < "offset" : [0, 275], < "parallax" : 2.5, < "directives" : "?saturation=-5?brightness=10", < "fadePercent" : 0.03 --- > "kind" : "godrays", > "offset" : [0, 90], > "parallax" : 1.211, > "unlit" : true 39,43c49,51 < "kind" : "foliage/treefront", < "offset" : [0, 255], < "parallax" : 3, < "directives" : "?saturation=-10?brightness=5", < "fadePercent" : 0.03 --- > "kind" : "forestlargetree", > "offset" : [0, 90], > "parallax" : 1.2 46,50c54,72 < "kind" : "foliage/treeback", < "offset" : [0, 235], < "parallax" : 3.5, < "directives" : "?saturation=-5?brightness=10", < "fadePercent" : 0.03 --- > "kind" : "forestcanopy", > "offset" : [0, -175], > "parallax" : 1.31, > "directives" : "?saturation=-60" > }, > { > "kind" : "forestsmalltree", > "offset" : [0, 90], > "parallax" : 1.3 > }, > { > "kind" : "forestsmalltree", > "offset" : [0, 90], > "parallax" : 1.4 > }, > { > "kind" : "foresttreesback", > "offset" : [0, 400], > "parallax" : 1.5 55,57c77,78 < "parallax" : 9, < "directives" : "?brightness=20?saturation=-10", < "fadePercent" : 0.35 --- > "parallax" : 4, > "fadePercent" : 0.1 58a80 > scripts\behavior.lua 0a1 > require "/scripts/util.lua" 321,338d321 < ----------------------------------------------------------- < -- HELPERS < ----------------------------------------------------------- < < function parseArgs(args, defaults) < local parsed = {} < for k,v in pairs(args) do < parsed[k] = v < end < for k,v in pairs(defaults) do < if parsed[k] == nil then < parsed[k] = v < end < end < return parsed < end < < 431c414,419 < end \ No newline at end of file --- > end > > function notify(notification) > table.insert(self.notifications, notification) > return true > end scripts\pathing.lua 0a1,2 > require "/scripts/util.lua" > 6,16c8,17 < -------------------------------------------------------------------------------- < -- Valid options: < -- openDoorCallback: function that will be passed a door entity id and should < -- return true if the door can be opened < -- run: whether npc should run < function moveTo(targetPosition, dt, options) < if options == nil then options = {} end < local pathOptions = { < returnBest = options.returnBest, < mustEndOnGround = options.mustEndOnGround, < maxDistance = options.maxDistance or 80, --- > > Pather = {} > > function Pather:new(options) > options = options or {} > local newPather = {} > newPather.pathOptions = parseArgs(options.pathOptions or {}, { > returnBest = false, > mustEndOnGround = false, > maxDistance = 80, 29,47c30,34 < } < if options.run == nil then options.run = false end < if self.pathing.deltaX == nil then self.pathing.deltaX = 1 end < self.moved = true < < local position = mcontroller.position() < local boundBox = mcontroller.boundBox() < < --Really close to the target position, probably don't need to path < local targetDistance = world.magnitude(targetPosition, position) < local toTarget = world.distance(targetPosition, position) < if targetDistance < 2 and math.abs(toTarget[2]) < 1 then < return approachTargetPosition(targetPosition, options.run) < end < < targetPosition = { < math.floor(targetPosition[1]) + 0.5, < math.floor(targetPosition[2]) + 0.5 < } --- > }) > newPather.options = parseArgs(options, { > run = false > }) > newPather.deltaX = 1 49c36,37 < script.setUpdateDelta(self.updateDelta or 5) --- > newPather.position = mcontroller.position() > newPather.lastPosition = newPather.position 51,55c39 < --Find new path if target position has changed < local pathTargetPosition = self.pathing.targetPosition < if pathTargetPosition == nil or self.pathing.path == nil or < targetPosition[1] ~= pathTargetPosition[1] or < targetPosition[2] ~= pathTargetPosition[2] then --- > newPather.boundBox = mcontroller.boundBox() 57,65c41,43 < if not findPath(targetPosition, pathOptions) then < world.debugText("findPath failed", {position[1], position[2]-2}, "red") < return false < elseif self.pathing.path.currentEdge == nil then < --The found path is empty, try again later < self.pathing.path = nil < return false < end < end --- > setmetatable(newPather, extend(self)) > return newPather > end 67,73c45,47 < local path = updatePath(targetPosition, pathOptions) < if not path then < world.debugText("updatePath failed", {position[1], position[2]-2}, "red") < -- New path is found on the next call to moveTo < return false < end < if self.debug then debugPath(path) end --- > function Pather:move(targetPosition, dt) > local run = self.options.run > if run == true then run = mcontroller.movingDirection() == mcontroller.facingDirection() end 75,79c49,58 < if path.currentEdge == nil then < --Reached the end of the path on this update < --Find a new path right away < self.pathing.path = nil < return false --- > self.lastPosition = self.position > self.position = mcontroller.position() > self.run = run > self.targetPosition = targetPosition > self.targetDistance = world.magnitude(targetPosition, self.position) > self.toTarget = world.distance(targetPosition, self.position) > self.dt = dt > > if self.targetDistance < 2 and math.abs(self.toTarget[2]) < 1 then > return self:approachTargetPosition() 82c61 < if self.pathing.downHoldTimer ~= nil then --- > if self.downHoldTimer ~= nil then 87,89c66,79 < local currentEdge = path.currentEdge < local nextPathPosition = currentEdge.target.position < local action = currentEdge.action --- > self:updatePath() > > if self.path == nil then > self:findPath() > if self.path == nil then > if self.lastPosition[1] == self.position[1] and self.lastPosition[2] == self.position[2] then > return false > else > return "running" > end > end > end > > debugPath(self.path) 91,92c81,82 < local nextEdge = path.path[path.currentEdgeIndex+2] or {} < local nextAction = nextEdge.action or "None" --- > return self:edgeMove() > end 94c84,88 < if nextAction == "Jump" or nextAction == "Drop" then --- > function Pather:approachTargetPosition() > if math.abs(self.toTarget[1]) < tickMoveDistance() and math.abs(self.toTarget[2]) < 1 then > return true > else > moveX(self.toTarget[1], run) 95a90,118 > return "running" > end > end > > function Pather:findPath() > if not mcontroller.onGround() then > return "running" > end > self.path = entity.findPath(self.targetPosition, self.pathOptions) > if self.path ~= nil then > self.pathTarget = self.targetPosition > return true > end > return false > end > > function Pather:updatePath() > --Find a new path if the target position has changed > if self.pathTarget and world.magnitude(self.targetPosition, self.pathTarget) > 4 then > self.path = nil > end > > > if self.path == nil then return false end > self.path = entity.updatePath(self.path) > > --Find a new path if we don't have one > if self.path == nil or self.path.currentEdge == nil or self.path.path[self.path.currentEdgeIndex+2] == nil then > self.path = nil 96a120,129 > end > > function Pather:advancePath() > self.path.currentEdgeIndex = self.path.currentEdgeIndex + 1 > self.path.currentEdge = self.path.path[self.path.currentEdgeIndex+1] > end > > function Pather:edgeMove() > setMoved(true) > self.edge = self.path.currentEdge 98c131,133 < local delta = world.distance(nextPathPosition, position) --- > self.nextPathPosition = self.edge.target.position > self.action = self.edge.action > self.delta = world.distance(self.nextPathPosition, self.position) 100c135 < local edgeDelta = world.distance(currentEdge.target.position, currentEdge.source.position) --- > local edgeDelta = world.distance(self.edge.target.position, self.edge.source.position) 102c137 < self.pathing.deltaX = edgeDelta[1] > 0 and 1 or -1 --- > self.deltaX = edgeDelta[1] > 0 and 1 or -1 105,107c140,159 < -- Open doors in the way < local xOffset = delta[1] > 0 and boundBox[3] + 1 or boundBox[1] - 1 < local line = {position, { position[1] + xOffset, position[2]}} --- > world.debugText(self.action, {self.position[1], self.position[2]-2}, "blue") > > if self.action == "Swim" then > return self:moveSwim() > elseif self.action == "Jump" then > return self:moveJump() > elseif self.action == "Drop" then > return self:moveDrop() > elseif self.action == "Arc" then > return self:moveArc() > elseif self.action == "Land" then > return self:moveLand() > else > return self:moveWalk() > end > end > > function Pather:openDoors() > local xOffset = delta[1] > 0 and self.boundBox[3] + 1 or self.boundBox[1] - 1 > local line = {self.position, { self.position[1] + xOffset, self.position[2]}} 119c171 < if options.openDoorCallback == nil or options.openDoorCallback(closedDoorId) then --- > if self.options.openDoorCallback == nil or self.options.openDoorCallback(closedDoorId) then 124a177 > end 126,151c179,185 < if self.debug then < world.debugText(action, {position[1], position[2]-2}, "blue") < end < < if action ~= "Arc" then < self.pathing.arcPreviousX = nil < self.pathing.arcPreviousY = nil < self.pathing.wasOffGround = false < end < < if action == "Swim" then < if delta[2] > 0 then < mcontroller.controlJump() < mcontroller.controlHoldJump() < end < moveX(delta[1], options.run) < return "running" < < elseif action == "Jump" then < script.setUpdateDelta(1) < < --Approach the jump position more precisely to follow the jump arc accurately < if math.abs(delta[1]) > tickMoveDistance() then < moveX(delta[1], options.run) < return "running" < end --- > function Pather:timedDrop(time) > if holdTime == nil then holdTime = 0 end > holdTime = math.min(holdTime, 0.5) > mcontroller.controlDown() > self.downHoldTimer = holdTime > script.setUpdateDelta(1) > end 153,170c187,189 < if mcontroller.liquidMovement() or not mcontroller.onGround() then < if not self.pathing.jumped then < -- If we haven't quite landed yet, wait until we do < mcontroller.setXVelocity(0) < if mcontroller.liquidMovement() and delta[2] >= 0 then < -- Jump point is above us, swim up to it < mcontroller.controlJump() < mcontroller.controlHoldJump() < end < return "running" < else < -- We've jumped and we're no longer on the ground. Move to the next node < mcontroller.setXVelocity(currentEdge.jumpVelocity[1]) < advancePath() < self.pathing.jumped = nil < return "running" < end < end --- > function Pather:keepDropping() > if self.downHoldTimer ~= nil then > mcontroller.controlDown() 172,175c191,193 < -- Teleport to node position for very accurate jumping < -- Teleporting should be kept unnoticeable < if not self.pathing.jumped then < mcontroller.setPosition(currentEdge.source.position) --- > self.downHoldTimer = self.downHoldTimer - dt > if self.downHoldTimer <= 0 or mcontroller.onGround() then > self.downHoldTimer = nil 176a195,196 > end > end 178,186c198,205 < -- Keep applying the Y jump velocity until we're no longer on the ground. < -- Things like groundSlideMovement mess with our velocity until we're < -- in the air (we do however still want groundSlideMovement to apply when < -- we're not jumping so disabling it altogether is not ideal). < -- X velocity is applied when we're clear of the ground. < local padding = 1 < jump({0, currentEdge.jumpVelocity[2] + padding}) < self.pathing.jumped = true < return "running" --- > function Pather:moveSwim() > if self.delta[2] > 0 then > mcontroller.controlJump() > mcontroller.controlHoldJump() > end > moveX(self.delta[1], self.run) > return "running" > end 188,195c207,208 < elseif action == "Drop" then < script.setUpdateDelta(1) < if math.abs(delta[1]) > tickMoveDistance() then < moveX(delta[1], options.run) < return "running" < else < mcontroller.setPosition({nextPathPosition[1], position[2]}) < end --- > function Pather:moveJump() > script.setUpdateDelta(1) 197,199c210,212 < timedDrop(math.max(timeToFall(-delta[2]), 0.05)) < mcontroller.setXVelocity(0) < advancePath() --- > --Approach the jump position more precisely to follow the jump arc accurately > if math.abs(self.delta[1]) > tickMoveDistance() then > moveX(self.delta[1], self.run) 200a214 > end 202,217c216,223 < elseif action == "Arc" then < self.pathing.jumped = false < < -- Advance path if the target position is in a different direction than the arc is pointing < if passedTarget(currentEdge) then < advancePath() < end < < if mcontroller.onGround() then < local nextEdge = path.path[path.currentEdgeIndex+2] or {} < if nextEdge.action and nextEdge.action ~= "Arc" then < advancePath() < elseif delta[2] > 0.5 then < self.pathing.path = nil < else < moveX(delta[1]) --- > if mcontroller.liquidMovement() or not mcontroller.onGround() then > if not self.jumped then > -- If we haven't quite landed yet, wait until we do > mcontroller.setXVelocity(0) > if mcontroller.liquidMovement() and self.delta[2] >= 0 then > -- Jump point is above us, swim up to it > mcontroller.controlJump() > mcontroller.controlHoldJump() 221,247c227,230 < --Try to not get slowed down by friction from air or walls < mcontroller.controlParameters({ < airFriction = 0, < liquidFriction = 0, < normalGroundFriction = 0, < ambulatingGroundFriction = 0 < }) < < -- setXVelocity in case that changes mid-jump (e.g. when jumping straight < -- up and then to the side). < if currentEdge.source.velocity then < mcontroller.setXVelocity(currentEdge.source.velocity[1]) < elseif currentEdge.target.velocity then < mcontroller.setXVelocity(currentEdge.target.velocity[1]) < else < mcontroller.setXVelocity(0) < end < < if mcontroller.liquidPercentage() > 0 then < if currentEdge.source.velocity and currentEdge.source.velocity[2] ~= 0 then < mcontroller.setYVelocity(currentEdge.source.velocity[2]) < else < advancePath() < end < end < < script.setUpdateDelta(1) --- > -- We've jumped and we're no longer on the ground. Move to the next node > mcontroller.setXVelocity(self.edge.jumpVelocity[1]) > self:advancePath() > self.jumped = nil 249a233 > end 251,255c235,250 < elseif action == "Land" then < if delta[2] > 0.5 then < self.pathing.path = nil < end < moveX(delta[1], options.run) --- > -- Teleport to node position for very accurate jumping > -- Teleporting should be kept unnoticeable > if not self.jumped then > mcontroller.setPosition(self.edge.source.position) > end > > -- Keep applying the Y jump velocity until we're no longer on the ground. > -- Things like groundSlideMovement mess with our velocity until we're > -- in the air (we do however still want groundSlideMovement to apply when > -- we're not jumping so disabling it altogether is not ideal). > -- X velocity is applied when we're clear of the ground. > local padding = 1 > jump({0, self.edge.jumpVelocity[2] + padding}) > self.jumped = true > return "running" > end 256a252,255 > function Pather:moveDrop() > script.setUpdateDelta(1) > if math.abs(self.delta[1]) > tickMoveDistance() then > moveX(self.delta[1], options.run) 259,280c258 < -- action is either "Walk" or "Fly" < -- TODO implement flying for flying NPCs (if there are any) < < if math.abs(delta[1]) < 0.25 and (delta[2] > boundBox[4] or delta[2] < boundBox[2]) then < -- Unable to walk vertically - something went wrong following this path, < -- so generate a new one < self.pathing.path = nil < return "running" < end < < -- Walking off a ledge < local nextEdge = path.path[path.currentEdgeIndex+2] or {} < if nextEdge.action and nextEdge.action == "Arc" then < script.setUpdateDelta(1) < if passedTarget(currentEdge) then < advancePath() < return "running" < end < end < < moveX(delta[1], options.run) < return "running" --- > mcontroller.setPosition({nextPathPosition[1], self.position[2]}) 282d259 < end 284,285c261,264 < function getExpirationTime(targetPosition) < return world.time() + 1 + math.random() --- > timedDrop(math.max(timeToFall(-self.delta[2]), 0.05)) > mcontroller.setXVelocity(0) > self:advancePath() > return "running" 288c267,268 < repathDistance = 5 --- > function Pather:moveArc() > self.jumped = false 290,295c270,272 < function findPath(targetPosition, options) < if self.pathing.expirationTime == nil then < self.pathing.expirationTime = 0 < end < if self.pathing.path and not mcontroller.onGround() and not world.liquidAt(mcontroller.position()) then < return self.pathing.path --- > -- Advance path if the target position is in a different direction than the arc is pointing > if passedTarget(self.edge) then > self:advancePath() 298,321c275,280 < local pathExpired = self.pathing.expirationTime < world.time() < if not pathExpired and self.pathing.targetPosition and not self.pathing.path then < -- Recalculate path if the path timed out, or if there was no previous path < -- or if the target has moved by repathDistance < local targetChange = world.distance(self.pathing.targetPosition, targetPosition) < targetChange[1] = math.abs(targetChange[1]) < targetChange[2] = math.abs(targetChange[2]) < pathExpired = math.abs(targetChange[1]) >= repathDistance or math.abs(targetChange[2]) >= repathDistance < end < < if pathExpired then < self.pathing.path = entity.findPath(targetPosition, options) < < -- Simple pathing benchmark < -- if self.debug then < -- local findPathCount = world.getProperty("findPathCount") or 0 < -- findPathCount = findPathCount + 1 < -- world.setProperty("findPathCount", findPathCount) < -- world.logInfo("Path count: %s | Path length: %s | In state: %s", findPathCount, self.pathing.path and #self.pathing.path.path, self.state and self.state.stateDesc()) < -- end < < if self.pathing.path == nil then < incrementStuckCount() < attemptUnsticking() --- > if mcontroller.onGround() then > local nextEdge = self.path.path[self.path.currentEdgeIndex+2] or {} > if nextEdge.action and nextEdge.action ~= "Arc" then > self:advancePath() > elseif self.delta[2] > 0.5 then > self.path = nil 323,324c282,301 < self.pathing.stuck = nil < self.pathing.stuckCount = 0 --- > moveX(self.delta[1], self.run) > end > return "running" > else > --Try to not get slowed down by friction from air or walls > mcontroller.controlParameters({ > airFriction = 0, > liquidFriction = 0, > normalGroundFriction = 0, > ambulatingGroundFriction = 0 > }) > > -- setXVelocity in case that changes mid-jump (e.g. when jumping straight > -- up and then to the side). > if self.edge.source.velocity then > mcontroller.setXVelocity(self.edge.source.velocity[1]) > elseif self.edge.target.velocity then > mcontroller.setXVelocity(self.edge.target.velocity[1]) > else > mcontroller.setXVelocity(0) 326,330d302 < self.pathing.targetPosition = targetPosition < self.pathing.expirationTime = getExpirationTime(targetPosition) < end < return self.pathing.path < end 332,343c304,309 < function updatePath(targetPosition, options) < -- Update path, pruning nodes < self.pathing.path = entity.updatePath(self.pathing.path) < < local path = self.pathing.path < if path then < -- If on the last node and the target position is farther away, find an updated path < -- This removes the delay between reaching the end of a path and starting a new one < local nextEdge = path.path[path.currentEdgeIndex+2] < if path.currentEdge and not nextEdge and world.magnitude(path.currentEdge.target.position, targetPosition) > 1 then < self.pathing.expirationTime = nil < path = findPath(targetPosition, options) or path --- > if mcontroller.liquidPercentage() > 0 then > if self.edge.source.velocity and self.edge.source.velocity[2] ~= 0 then > mcontroller.setYVelocity(self.edge.source.velocity[2]) > else > self:advancePath() > end 345,348d310 < else < -- Find a new path if the old path is invalid < path = findPath(self.pathing.targetPosition, options) < end 350c312,314 < return path --- > script.setUpdateDelta(1) > return "running" > end 353,358c317,321 < function advancePath() < local path = self.pathing.path < path.currentEdgeIndex = path.currentEdgeIndex + 1 < -- Index + 1 because Lua is 1-indexed < path.currentEdge = path.path[path.currentEdgeIndex+1] < end --- > function Pather:moveLand() > if self.delta[2] > 0.5 then > self.path = nil > end > moveX(self.delta[1], self.run) 360,365c323 < function attemptUnsticking() < -- Pathing is deterministic, so if we get stuck and try pathing again, we're < -- likely to end up stuck again. < -- This adds some randomness to try escaping obstacles that get us stuck. < moveX(math.random()-0.5, false) < script.setUpdateDelta(5) --- > return "running" 368,376c326,331 < function incrementStuckCount() < local position = mcontroller.position() < self.pathing.stuckCount = self.pathing.stuckCount or 0 < < --Reset stuck when moved to a new position < local moveDistance = world.magnitude(position, self.pathing.stuckPosition or position) < if moveDistance > entity.configParameter("pathing.stuckResetTolerance", 0.5) then < self.pathing.stuckCount = 0 < self.pathing.stuckPosition = nil --- > function Pather:moveWalk() > if math.abs(self.delta[1]) < 0.25 and (self.delta[2] > self.boundBox[4] or self.delta[2] < self.boundBox[2]) then > -- Unable to walk vertically - something went wrong following this path, > -- so generate a new one > self.path = nil > return "running" 379,382c334,341 < self.pathing.stuckCount = self.pathing.stuckCount + 1 < if self.pathing.stuckCount >= entity.configParameter("pathing.stuckCount", 5) then < self.pathing.stuck = true < self.pathing.stuckPosition = position --- > -- Walking off a ledge > local nextEdge = self.path.path[self.path.currentEdgeIndex+2] or {} > if nextEdge.action and nextEdge.action == "Arc" then > script.setUpdateDelta(1) > if passedTarget(self.edge) then > self:advancePath() > return "running" > end 383a343,345 > > moveX(self.delta[1], self.run) > return "running" 386,396c348,350 < function approachTargetPosition(targetPosition, run) < local position = mcontroller.position() < local targetDistance = world.magnitude(position, targetPosition) < local toTarget = world.distance(targetPosition, position) < if math.abs(toTarget[1]) < tickMoveDistance() and math.abs(toTarget[2]) < 1 then < return true < else < moveX(toTarget[1], run) < script.setUpdateDelta(1) < return "running" < end --- > function moveTo() > require("/scripts/oldpathing.lua") --replaces this moveTo function > return "running" 419,437c373,374 < --Drop and hold down for holdTime, also approach goalPosition < function timedDrop(holdTime, goalPosition) < if holdTime == nil then holdTime = 0 end < holdTime = math.min(holdTime, 0.5) < mcontroller.controlDown() < self.pathing.downHoldTimer = holdTime < script.setUpdateDelta(1) < end < < --Keep holding down < function keepDropping(dt, options) < if self.pathing.downHoldTimer ~= nil then < mcontroller.controlDown() < < self.pathing.downHoldTimer = self.pathing.downHoldTimer - dt < if self.pathing.downHoldTimer <= 0 or mcontroller.onGround() then < self.pathing.downHoldTimer = nil < end < end --- > function setMoved(moved) > self.moved = moved 468a406,409 > > if type(maxHeight) ~= "number" or type(minHeight) ~= "number" then > world.logInfo("%s %s", maxHeight, minHeight) > end scripts\util.lua 222a223,248 > > function parseArgs(args, defaults) > local parsed = {} > for k,v in pairs(args) do > parsed[k] = v > end > for k,v in pairs(defaults) do > if parsed[k] == nil then > parsed[k] = v > end > end > return parsed > end > > function extend(base) > return { > __index = function(t,k) > local raw = rawget(t,k) > if raw == nil then > return base[k] > else > return raw > end > end > } > end \ No newline at end of file scripts\actions\movement.lua 18a19,22 > if args.run == true then > args.run = mcontroller.movingDirection() == mcontroller.facingDirection() > end > 54a59,62 > if args.run == true then > args.run = mcontroller.movingDirection() == mcontroller.facingDirection() > end > 107a116 > local pather = Pather:new({run = args.run}) 118,119c127,128 < moved = moveTo(position, args.dt, {run = args.run}) < BData:setNumber(output.direction, util.toDirection(self.pathing.deltaX)) --- > moved = pather:move(position, args.dt) > BData:setNumber(output.direction, util.toDirection(pather.deltaX)) 121c130 < until moved == true or (moved == false and (self.pathing.stuck or args.failFast)) --- > until moved == true or moved == false scripts\actions\notification.lua 1,5d0 < function notify(notification) < table.insert(self.notifications, notification) < return true < end < 69c64 < end \ No newline at end of file --- > end scripts\actions\time.lua 4a5,6 > elseif type(time) == "string" then > return BData:getNumber(time) stagehands\coordinator.lua 16a17,19 > for _,task in pairs(self.tasks) do > task:leave(entityId) > end stagehands\coordinator\npccombat.lua 6a7,16 > local entityPosition = world.entityPosition(self.goal) > self.groupResources:set("targetPosition", entityPosition) > > if not self.npcBounds or not self.npcPoly then > if self.group.members[1] then > self.npcBounds = world.callScriptedEntity(self.group.members[1], "mcontroller.boundBox") > self.npcPoly = world.callScriptedEntity(self.group.members[1], "mcontroller.collisionPoly") > end > end > 7a18 > setRangedAttackerPositions() 13d23 < self.groupResources:set("targetPosition", entityPosition) 16c26 < if self.tasks["melee"] then --- > if self.tasks["melee"] and #self.tasks["melee"].members >= 1 then 22c32 < self.memberResources[melee[1]]:set("movePosition", vec2.add({dir * 1.5, 0}, entityPosition)) --- > self.memberResources[melee[1]]:set("movePosition", attackPositionAlongLine(vec2.add({dir * 1.5, 0}, entityPosition), entityPosition)) 25c35 < self.memberResources[melee[2]]:set("movePosition", vec2.add({dir * -1.5, 0}, entityPosition)) --- > self.memberResources[melee[2]]:set("movePosition", attackPositionAlongLine(vec2.add({dir * -1.5, 0}, entityPosition), entityPosition)) 31c41 < self.memberResources[melee[3]]:set("movePosition", vec2.add({dir * 3, 0}, entityPosition)) --- > self.memberResources[melee[3]]:set("movePosition", attackPositionAlongLine(vec2.add({dir * 3, 0}, entityPosition), entityPosition)) 35c45,96 < self.memberResources[melee[4]]:set("movePosition", vec2.add({dir * -3, 0}, entityPosition)) --- > self.memberResources[melee[4]]:set("movePosition", attackPositionAlongLine(vec2.add({dir * -3, 0}, entityPosition), entityPosition)) > end > end > end > > --Sets attack positions for all ranged attackers > function setRangedAttackerPositions() > local entityPosition = world.entityPosition(self.goal) > local maxRange = 25 > > local dirRanges = {} > dirRanges[-1] = maxRange > dirRanges[1] = maxRange > > if self.tasks["ranged"] then > for _,ranged in pairs(self.tasks["ranged"].members) do > local toRanged = world.distance(world.entityPosition(ranged), entityPosition) > local dir = util.toDirection(toRanged[1]) > local startLine = {entityPosition[1] + dir * dirRanges[dir], entityPosition[2]} > local attackPosition = attackPositionInRange(startLine, entityPosition) > self.memberResources[ranged]:set("movePosition", attackPosition) > > local toAttack = world.distance(attackPosition, entityPosition) > dirRanges[dir] = math.abs(toAttack[1]) - 2 > end > end > end > > function attackPositionAlongLine(startLine, endLine) > local toEnd = world.distance(endLine, startLine) > local dir = util.toDirection(toEnd[1]) > > while toEnd[1] * dir > 0 do > local groundPosition = findGroundAttackPosition(startLine, math.abs(toEnd[1]), endLine, self.npcBounds, self.npcPoly) > if groundPosition then > return groundPosition > end > startLine[1] = startLine[1] + dir > toEnd = world.distance(endLine, startLine) > end > > return endLine > end > > function attackPositionInRange(startLine, endLine) > local toEnd = world.distance(endLine, startLine) > local dir = util.toDirection(toEnd[1]) > > while toEnd[1] * dir > 0 do > local attackPosition = findAttackPositionInRange(math.abs(toEnd[1]), endLine, -dir, self.npcBounds, self.npcPoly) > if attackPosition then > return attackPosition 36a98,112 > startLine[1] = startLine[1] + dir > toEnd = world.distance(endLine, startLine) > end > > return endLine > end > > function validAttackPosition(position, bounds, collisionPoly) > local groundRegion = { > position[1] + bounds[1], position[2] + bounds[2] - 1, > position[1] + bounds[3], position[2] + bounds[2] > } > local collisionResolve = world.resolvePolyCollision(collisionPoly, position, 0.9) > if world.rectTileCollision(groundRegion, {"Null", "Block", "Dynamic", "Platform"}) and collisionResolve then > return collisionResolve 38a115,157 > > --Find a valid ground position > function findGroundAttackPosition(position, maxHeight, losPosition, bounds, collisionPoly) > position[2] = math.ceil(position[2]) - (bounds[2] % 1) > for y = 0, maxHeight do > --Check up > local validPosition = validAttackPosition({position[1], position[2] + y}, bounds, collisionPoly) > if validPosition and not world.lineTileCollision({position[1], position[2] + y}, losPosition) then > return validPosition > end > --Check down > if y ~= 0 then > validPosition = validAttackPosition({position[1], position[2] - y}, bounds, collisionPoly) > if validPosition and not world.lineTileCollision({position[1], position[2] - y}, losPosition) then > return validPosition > end > end > end > end > > function findAttackPositionInRange(range, center, direction, bounds, collisionPoly) > if range == 0 then return center end > > local step = (math.pi / 2) / range > for i = 0, range do > local x = range * math.cos(math.asin(i / range)) > local position = {center[1] + direction * x, center[2] + i} > position[2] = math.ceil(position[2]) - (bounds[2] % 1) > world.debugPoint(position, "yellow") > local validPosition = validAttackPosition(position, bounds, collisionPoly) > if validPosition and not world.lineTileCollision(position, center) then > return validPosition > end > > position[2] = center[2] - i > position[2] = math.ceil(position[2]) - (bounds[2] % 1) > world.debugPoint(position, "yellow") > validPosition = validAttackPosition(position, bounds, collisionPoly) > if validPosition and not world.lineTileCollision(position, center) then > return validPosition > end > end > end \ No newline at end of file stats\effects\npcreactions\nosebleed.animation 5c5 < "emissionRate" : 10.0, --- > "emissionRate" : 200.0, 17c17 < "timeToLive" : 1, --- > "timeToLive" : 0.2, 36c36 < "initialVelocity" : [10, 5], --- > "initialVelocity" : [3, 0], 41c41 < "timeToLive" : 1, --- > "timeToLive" : 0.2, 44c44 < "initialVelocity" : [3.0, 3.0] --- > "initialVelocity" : [3.0, 0] 53c53 < "emissionRate" : 10.0, --- > "emissionRate" : 200.0, 65c65 < "timeToLive" : 1, --- > "timeToLive" : 0.2, 84c84 < "initialVelocity" : [-10, 5], --- > "initialVelocity" : [-3, 0], 89c89 < "timeToLive" : 1, --- > "timeToLive" : 0.2, 92c92 < "initialVelocity" : [3.0, 3.0] --- > "initialVelocity" : [3.0, 0] stats\effects\npcreactions\vomit.animation 36c36 < "initialVelocity" : [10, 5], --- > "initialVelocity" : [3, 0], 44c44 < "initialVelocity" : [3.0, 3.0] --- > "initialVelocity" : [3.0, 0] 60c60 < "initialVelocity" : [10, 5], --- > "initialVelocity" : [3, 0], 68c68 < "initialVelocity" : [3.0, 3.0] --- > "initialVelocity" : [3.0, 0] 108c108 < "initialVelocity" : [-10, 5], --- > "initialVelocity" : [-3, 0], 132c132 < "initialVelocity" : [-10, 5], --- > "initialVelocity" : [-3, 0], tenants\villager_human2.tenant 7c7 < "door": 1, --- > "door": 1