├── tests ├── ObjectReader.hx ├── Auto.hx └── Node.hx ├── script.hxml ├── targets ├── cpp.hxml ├── php.hxml ├── python.hxml ├── lua.hxml ├── cs.hxml ├── java.hxml ├── nodejs.hxml ├── c.hxml └── set.hxml ├── lines.sh ├── map.png ├── setup_data_client.hxml ├── setup_data_server.hxml ├── .haxerc ├── icon.png ├── logo.png ├── server.hl ├── test_transition.hxml ├── mysteraV1.png ├── mysteraV1Test.png ├── openlife ├── client │ ├── ClientSettings.hx │ ├── Main.hx │ ├── Game.hx │ ├── Sound.hx │ ├── World.hx │ ├── ClientTag.hx │ ├── BinPack.hx │ ├── Object.hx │ └── Fps.hx ├── auto │ ├── AiPx.hx │ ├── BotType.hx │ ├── Say.hx │ ├── actions │ │ ├── Idle.hx │ │ ├── Use.hx │ │ ├── Eat.hx │ │ ├── Take.hx │ │ ├── Travel.hx │ │ └── Search.hx │ ├── Overseer.hx │ ├── Ai.hx │ ├── Interpreter.hx │ ├── Action.hx │ ├── roles │ │ └── BerryEater.hx │ ├── WorldInterface.hx │ ├── Role.hx │ ├── Automation.hx │ ├── PlayerInterface.hx │ └── ObjectCode.hx ├── data │ ├── Target.hx │ ├── window │ │ └── Window.hx │ ├── sound │ │ ├── SoundType.hx │ │ └── SoundData.hx │ ├── object │ │ ├── ObjectType.hx │ │ ├── ToolSetRecord.hx │ │ ├── ObjectKey.hx │ │ ├── player │ │ │ └── PlayerMove.hx │ │ └── SpriteData.hx │ ├── animation │ │ ├── emote │ │ │ ├── Emotion.hx │ │ │ └── EmoteData.hx │ │ ├── AnimationType.hx │ │ ├── SoundParameter.hx │ │ ├── AnimationRecord.hx │ │ ├── AnimationParameter.hx │ │ └── AnimationData.hx │ ├── Pos.hx │ ├── map │ │ ├── SceneData.hx │ │ ├── MapInstance.hx │ │ ├── MapChange.hx │ │ ├── SceneCell.hx │ │ └── MapData.hx │ ├── Rectangle.hx │ ├── Point.hx │ ├── ArrayDataInt.hx │ ├── ArrayData.hx │ ├── transition │ │ └── Category.hx │ ├── FractalNoise.hx │ └── LineReader.hx ├── resources │ ├── ObjectSkim.hx │ ├── Emote.hx │ ├── Resource.hx │ └── ObjectBake.hx ├── engine │ ├── Utility.hx │ ├── EngineEvent.hx │ └── EngineHeader.hx ├── macros │ ├── ScriptExist.hx │ ├── Debug.hx │ └── Macro.hx ├── server │ ├── ServerHeader.hx │ ├── ServerTag.hx │ ├── ServerAi.hx │ ├── ThreadServer.hx │ ├── SerializeHelper.hx │ ├── ScoreEntry.hx │ └── Biome.hx ├── settings │ ├── OpenLifeData.hx │ └── Settings.hx ├── .vscode │ └── launch.json └── graphics │ └── TgaData.hx ├── app.hxml ├── exclude_list.txt ├── hxformat.json ├── haxelib.json ├── .vscode ├── settings.json ├── settings - Copy.json └── tasks.json ├── server_neko.hxml ├── client.hxml ├── haxe_libraries ├── hxcpp.hxml ├── format.hxml ├── hxtsdgen.hxml ├── hxcs.hxml └── hxnodejs.hxml ├── app ├── Main.hx ├── Bake.hx ├── App.hx └── ClientAi.hx ├── docs.hxml ├── scripts ├── SetupDataServer.hx ├── SetupDataClient.hx ├── HLDebug.hx ├── FileTools.hx ├── Generate.hx ├── Docs.hx ├── SpriteReuse.hx ├── SetupData.hx ├── ProtocolTagCheck.hx └── Transition.hx ├── server.hxml ├── targets.hxml ├── .github └── workflows │ └── test.yml ├── objectDescTags.txt ├── .gitignore ├── package.json ├── BUILDING.MD ├── LICENSE ├── README.MD ├── WebServer └── OpenLifeReborn.html ├── FAQ.MD ├── icon.svg └── badge.svg /tests/ObjectReader.hx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /script.hxml: -------------------------------------------------------------------------------- 1 | -cp app 2 | --interp -------------------------------------------------------------------------------- /targets/cpp.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -cpp export/cpp -------------------------------------------------------------------------------- /targets/php.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -php export/php -------------------------------------------------------------------------------- /lines.sh: -------------------------------------------------------------------------------- 1 | git ls-files | grep -v -f exclude_list.txt | xargs wc -l -------------------------------------------------------------------------------- /map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/map.png -------------------------------------------------------------------------------- /setup_data_client.hxml: -------------------------------------------------------------------------------- 1 | -main scripts.SetupDataClient 2 | --interp -------------------------------------------------------------------------------- /setup_data_server.hxml: -------------------------------------------------------------------------------- 1 | -main scripts.SetupDataServer 2 | --interp -------------------------------------------------------------------------------- /.haxerc: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.2.5", 3 | "resolveLibs": "scoped" 4 | } -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/icon.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/logo.png -------------------------------------------------------------------------------- /server.hl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/server.hl -------------------------------------------------------------------------------- /targets/python.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -python export/python/openlife.py -------------------------------------------------------------------------------- /test_transition.hxml: -------------------------------------------------------------------------------- 1 | -cp scripts 2 | -main Transition 3 | -hl transition.hl -------------------------------------------------------------------------------- /mysteraV1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/mysteraV1.png -------------------------------------------------------------------------------- /mysteraV1Test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PXshadow/OpenLife/HEAD/mysteraV1Test.png -------------------------------------------------------------------------------- /targets/lua.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -D luajit 3 | #-D lua-vanilla 4 | -lua export/lua/openlife.lua -------------------------------------------------------------------------------- /targets/cs.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -D net-ver=40 3 | -D no-compilation 4 | -D real-position 5 | -cs export/cs -------------------------------------------------------------------------------- /openlife/client/ClientSettings.hx: -------------------------------------------------------------------------------- 1 | class ClientSettings { 2 | public static var SaveDirectory = "SaveFiles"; 3 | } -------------------------------------------------------------------------------- /targets/java.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | --jvm export/java/openlife.jar 3 | 4 | --next 5 | targets/set.hxml 6 | -java export/java -------------------------------------------------------------------------------- /openlife/auto/AiPx.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | using StringTools; 4 | using openlife.auto.AiHelper; 5 | 6 | class AiPx extends AiBase {} 7 | -------------------------------------------------------------------------------- /openlife/data/Target.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | @:expose 4 | typedef Target = { 5 | var targetId:Int; 6 | var x:Int; 7 | var y:Int; 8 | } 9 | -------------------------------------------------------------------------------- /app.hxml: -------------------------------------------------------------------------------- 1 | --macro openlife.macros.ScriptExist.run() 2 | --macro openlife.macros.Debug.run() 3 | -cp app 4 | -main Main 5 | -D app 6 | -hl app.hl 7 | -lib format -------------------------------------------------------------------------------- /exclude_list.txt: -------------------------------------------------------------------------------- 1 | txt 2 | svg 3 | MD 4 | png 5 | xml 6 | .DS_Store 7 | .gitignore 8 | .bat 9 | .yml 10 | .md 11 | .json 12 | .cmd 13 | .sh 14 | src/Secret.hx -------------------------------------------------------------------------------- /hxformat.json: -------------------------------------------------------------------------------- 1 | { 2 | "sameLine": { 3 | "ifBody": "same", 4 | "elseBody": "same", 5 | "ifElse": "next", 6 | "elseIf": "same" 7 | } 8 | } -------------------------------------------------------------------------------- /openlife/data/window/Window.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.window; 2 | 3 | @:expose 4 | typedef Window = { 5 | frameRate:Int, 6 | borderless:Bool, 7 | width:Int, 8 | height:Int 9 | } 10 | -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "OpenLife", 3 | "url" : "https://github.com/pxshadow/openlife", 4 | "contributors" : ["PXshadow"], 5 | "description" : "A OneLife engine in haxe", 6 | "version" : "0.0.1" 7 | } -------------------------------------------------------------------------------- /openlife/data/sound/SoundType.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.sound; 2 | 3 | @:expose 4 | enum SoundType { 5 | creation; 6 | // using conflicts with haxe 7 | use; 8 | eating; 9 | decay; 10 | } 11 | -------------------------------------------------------------------------------- /openlife/resources/ObjectSkim.hx: -------------------------------------------------------------------------------- 1 | package openlife.resources; 2 | 3 | /** 4 | * Skim through the object and remove all unneeded parts of the object for parsing 5 | */ 6 | @:expose 7 | class ObjectSkim {} 8 | -------------------------------------------------------------------------------- /openlife/data/object/ObjectType.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.object; 2 | 3 | /** 4 | * Type of object 5 | */ 6 | @:expose 7 | enum ObjectType { 8 | OBJECT; 9 | FLOOR; 10 | PLAYER; 11 | GROUND; 12 | } 13 | -------------------------------------------------------------------------------- /targets/nodejs.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | -lib hxnodejs 3 | -lib hxtsdgen 4 | -D js-es6 5 | #-D shallow-expose 6 | -D hxtsdgen_skip_header 7 | -D hxtsdgen_enums_ts 8 | -js index.js 9 | # does not work because of Thread usage -------------------------------------------------------------------------------- /openlife/data/object/ToolSetRecord.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.object; 2 | 3 | /** 4 | * Toolset record 5 | */ 6 | @:expose 7 | typedef ToolSetRecord = { 8 | setTag:String, 9 | setMembership:Array 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[haxe]": { 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.sortImports": "never" 6 | } 7 | }, 8 | "testExplorer.codeLens": false 9 | } -------------------------------------------------------------------------------- /server_neko.hxml: -------------------------------------------------------------------------------- 1 | -main openlife.server.Server 2 | #-hl export/server/server.hl 3 | #-cpp export/server 4 | -D analyzer-optimize 5 | -dce full 6 | --macro openlife.macros.Debug.run() 7 | #--interp 8 | -lib format 9 | -neko server.n -------------------------------------------------------------------------------- /client.hxml: -------------------------------------------------------------------------------- 1 | -cp openlife/client 2 | -main Main 3 | -lib heaps 4 | -lib hlsdl 5 | -dce full 6 | -lib hlopenal 7 | -D sound 8 | -hl client.hl 9 | -D resourcesPath=OneLifeData7 10 | -D windowsSize=1600x900 11 | -D windowTitle=client -------------------------------------------------------------------------------- /.vscode/settings - Copy.json: -------------------------------------------------------------------------------- 1 | { 2 | "[haxe]": { 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.sortImports": true 6 | } 7 | }, 8 | "testExplorer.codeLens": false 9 | } -------------------------------------------------------------------------------- /haxe_libraries/hxcpp.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "haxelib:/hxcpp#4.1.15" into hxcpp/4.1.15/haxelib 2 | # @run: haxelib run-dir hxcpp ${HAXE_LIBCACHE}/hxcpp/4.1.15/haxelib 3 | -cp ${HAXE_LIBCACHE}/hxcpp/4.1.15/haxelib/ 4 | -D hxcpp=4.1.15 -------------------------------------------------------------------------------- /targets/c.hxml: -------------------------------------------------------------------------------- 1 | targets/set.hxml 2 | 3 | #This produces files in the out directory, which can be compiled to a native executable with a C compiler such as gcc. 4 | #example gcc -O3 -o hello -std=c++ -I out out/main.c -lhl [-L/path/to/required/hdll] -------------------------------------------------------------------------------- /app/Main.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openlife.engine.Engine; 4 | 5 | class Main { 6 | public static function main() { 7 | Sys.println("Starting OpenLife App Client" #if debug + " in debug mode" #end); 8 | new App(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs.hxml: -------------------------------------------------------------------------------- 1 | -D doc-gen 2 | -D app 3 | -cp scripts 4 | -lib format 5 | --macro scripts.FileTools.deleteDir("docs/api") 6 | -cp app 7 | -main Main 8 | --no-output 9 | --interp 10 | -xml xml/Terminal.xml 11 | --next 12 | -cp scripts 13 | -main Docs 14 | --interp -------------------------------------------------------------------------------- /scripts/SetupDataServer.hx: -------------------------------------------------------------------------------- 1 | package scripts; 2 | 3 | import scripts.SetupData; 4 | 5 | class SetupDataServer { 6 | public static function main() { 7 | new SetupDataServer(); 8 | } 9 | 10 | public function new() { 11 | SetupGameData(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /openlife/data/animation/emote/Emotion.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation.emote; 2 | 3 | @:expose 4 | typedef Emotion = { 5 | triggerWord:String, 6 | eyeEmot:Int, 7 | mouthEmot:Int, 8 | otherEmot:Int, 9 | faceEmot:Int, 10 | bodyEmot:Int, 11 | headEmot:Int 12 | } 13 | -------------------------------------------------------------------------------- /targets/set.hxml: -------------------------------------------------------------------------------- 1 | 2 | #--macro include("openlife") 3 | #--macro exclude("openlife.server") 4 | openlife.engine.Engine 5 | #-main openlife.engine.Engine 6 | -lib format 7 | #effects cs, java, cpp, hashlink 8 | -D no-compilation 9 | #set header 10 | -D source-header=OpenLife -------------------------------------------------------------------------------- /server.hxml: -------------------------------------------------------------------------------- 1 | -main openlife.server.Server 2 | #-hl export/server/server.hl 3 | #-cpp export/server 4 | -D analyzer-optimize 5 | #-D debug 6 | -dce full 7 | --macro openlife.macros.Debug.run() 8 | #--interp 9 | -D server 10 | -D no-deprecation-warnings 11 | -lib format 12 | -hl server.hl -------------------------------------------------------------------------------- /scripts/SetupDataClient.hx: -------------------------------------------------------------------------------- 1 | package scripts; 2 | 3 | import scripts.SetupData; 4 | 5 | class SetupDataClient { 6 | public static function main() { 7 | new SetupDataClient(); 8 | } 9 | 10 | public function new() { 11 | SetupGameData(); 12 | SetupGameSourceData(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /haxe_libraries/format.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/HaxeFoundation/format#ec1966add874b0ec778f30b4e83d8f1cdc09ff24" into format/3.5.0/github/ec1966add874b0ec778f30b4e83d8f1cdc09ff24 2 | -cp ${HAXE_LIBCACHE}/format/3.5.0/github/ec1966add874b0ec778f30b4e83d8f1cdc09ff24/ 3 | -D format=3.5.0 -------------------------------------------------------------------------------- /openlife/engine/Utility.hx: -------------------------------------------------------------------------------- 1 | package openlife.engine; 2 | 3 | import sys.FileSystem; 4 | import sys.io.File; 5 | 6 | @:expose 7 | class Utility { 8 | public static function dir():String { 9 | if (!FileSystem.exists("dir")) File.saveContent("dir", "OneLifeData7/"); 10 | return File.getContent("dir"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /openlife/macros/ScriptExist.hx: -------------------------------------------------------------------------------- 1 | package openlife.macros; 2 | 3 | import haxe.macro.Compiler; 4 | #if (!display && macro) 5 | import sys.FileSystem; 6 | 7 | class ScriptExist { 8 | public static macro function run() { 9 | if (FileSystem.exists("Script.hx")) Compiler.define("script"); 10 | return null; 11 | } 12 | } 13 | #end 14 | -------------------------------------------------------------------------------- /openlife/data/animation/AnimationType.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation; 2 | 3 | @:expose 4 | enum AnimationType { 5 | ground; 6 | held; 7 | moving; 8 | // special case of ground 9 | // for person who is now holding something 10 | // animation that only applies to a person as they eat something 11 | eating; 12 | doing; 13 | endAnimType; 14 | } 15 | -------------------------------------------------------------------------------- /openlife/auto/BotType.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.engine.Engine; 4 | import ClientAi.Bot; 5 | 6 | class BotType extends #if app Bot #else Engine #end 7 | { 8 | public var currentAction:Action; 9 | public var lastAction:Action; 10 | public var role:Role; 11 | public var currentTarget:String; 12 | public var lastTarget:String; 13 | } 14 | -------------------------------------------------------------------------------- /haxe_libraries/hxtsdgen.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/elsassph/hxtsdgen#67c50d2d96f64d6e2fe3504e85b274241c103cc3" into hxtsdgen/0.3.0/github/67c50d2d96f64d6e2fe3504e85b274241c103cc3 2 | -cp ${HAXE_LIBCACHE}/hxtsdgen/0.3.0/github/67c50d2d96f64d6e2fe3504e85b274241c103cc3/src 3 | -D hxtsdgen=0.3.0 4 | --macro hxtsdgen.Generator.use() 5 | -D use-rtti-doc 6 | -------------------------------------------------------------------------------- /targets.hxml: -------------------------------------------------------------------------------- 1 | #-cmd echo C++ 2 | #targets/cpp.hxml 3 | --next 4 | -cmd echo Java 5 | targets/java.hxml 6 | --next 7 | -cmd echo NodeJs 8 | targets/nodejs.hxml 9 | --next 10 | -cmd echo Lua 11 | targets/lua.hxml 12 | --next 13 | -cmd echo Php 14 | targets/php.hxml 15 | --next 16 | -cmd echo Python 17 | targets/python.hxml 18 | --next 19 | -cmd echo C# 20 | targets/cs.hxml -------------------------------------------------------------------------------- /tests/Auto.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openlife.auto.BotType; 4 | import openlife.engine.Engine; 5 | import openlife.auto.actions.Take; 6 | import openlife.auto.roles.*; 7 | import openlife.auto.Action; 8 | import openlife.auto.Role; 9 | 10 | function main() { 11 | var bot = new BotType(null); 12 | var role = new BerryEater(); 13 | new Take().step(bot); 14 | } 15 | -------------------------------------------------------------------------------- /openlife/auto/Say.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | @:expose 4 | class Say { 5 | public static function has(text:String, sub:String):Bool { 6 | return text.indexOf(sub) > -1; 7 | } 8 | 9 | public static function any(text:String, list:Array):Bool { 10 | for (sub in list) { 11 | if (has(text, sub)) return true; 12 | } 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /openlife/data/Pos.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | @:expose 4 | class Pos { 5 | public var x:Int; 6 | public var y:Int; 7 | 8 | public function new(x:Int = 0, y:Int = 0) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | public function clone():Pos { 14 | return new Pos(x, y); 15 | } 16 | 17 | public function toString():String { 18 | return '($x,$y)'; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /openlife/server/ServerHeader.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | /* 4 | interface ServerHeader 5 | { 6 | public function keepAlive():Void; 7 | public function login():Void; 8 | public function rlogin():Void; 9 | public function die():Void; 10 | public function emote(id:Int):Void; 11 | public function flip():Void; 12 | 13 | public var player:GlobalPlayerInstance; 14 | } 15 | */ 16 | -------------------------------------------------------------------------------- /openlife/auto/actions/Idle.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.auto.Action; 4 | 5 | class Idle extends openlife.auto.Action { 6 | public function new() { 7 | this.name = 'Idle'; 8 | } 9 | 10 | override public function step(bot:BotType) { 11 | // call this.work(); 12 | } 13 | 14 | override public function work(bot:BotType) { 15 | // Do nothing 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /openlife/data/map/SceneData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.map; 2 | 3 | import haxe.ds.Vector; 4 | 5 | @:expose 6 | class SceneData { 7 | public var width:Int = 0; 8 | public var height:Int = 0; 9 | public var cells:Vector; 10 | 11 | public function new(width:Int, height:Int) { 12 | this.width = width; 13 | this.height = height; 14 | cells = new Vector(width * height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /haxe_libraries/hxcs.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/HaxeFoundation/hxcs#c82f5c9acd0821467fe5799093dde95e43eb7d22" into hxcs/4.0.0-alpha/github/c82f5c9acd0821467fe5799093dde95e43eb7d22 2 | # @run: haxelib run-dir hxcs ${HAXE_LIBCACHE}/hxcs/4.0.0-alpha/github/c82f5c9acd0821467fe5799093dde95e43eb7d22 3 | -cp ${HAXE_LIBCACHE}/hxcs/4.0.0-alpha/github/c82f5c9acd0821467fe5799093dde95e43eb7d22/ 4 | -D hxcs=4.0.0-alpha -------------------------------------------------------------------------------- /openlife/data/Rectangle.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | @:expose 4 | class Rectangle { 5 | public var x:Int; 6 | public var y:Int; 7 | public var width:Int; 8 | public var height:Int; 9 | 10 | public function new(x:Int = 0, y:Int, width:Int = 0, height:Int = 0) { 11 | this.x = x; 12 | this.y = y; 13 | this.width = width; 14 | this.height = height; 15 | } 16 | 17 | public function toString():String { 18 | return 'x $x y $y ($width $height)'; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /openlife/client/Main.hx: -------------------------------------------------------------------------------- 1 | function main() { 2 | new Main(); 3 | } 4 | 5 | class Main extends hxd.App { 6 | override function init() { 7 | super.init(); 8 | Game.s2d = s2d; 9 | Game.engineHeaps = engine; 10 | Game.sevents = sevents; 11 | Game.init(); 12 | } 13 | 14 | override function update(dt:Float) { 15 | super.update(dt); 16 | Game.update(dt); 17 | } 18 | override function onResize() { 19 | super.onResize(); 20 | Game.resize(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /openlife/auto/actions/Use.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.auto.Action; 4 | 5 | class Use extends openlife.auto.Action { 6 | this.name = 'Use'; 7 | public function isValidAction() { 8 | // check stuff like distance or if path can be calculated. 9 | } 10 | 11 | public function step() { 12 | // check isValidAction and other validators if present 13 | // then call this.work(); 14 | } 15 | 16 | public function work() { 17 | // use object in hand 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /openlife/data/Point.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | @:expose 4 | class Point { 5 | /** 6 | * Float value x 7 | */ 8 | public var x:Float = 0; 9 | 10 | /** 11 | * Float value y 12 | */ 13 | public var y:Float = 0; 14 | 15 | /** 16 | * set new point 17 | * @param x 18 | * @param y 19 | */ 20 | public function new(x:Float = 0, y:Float = 0) { 21 | this.x = x; 22 | this.y = y; 23 | } 24 | 25 | public function clone():Point { 26 | return new Point(x, y); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /haxe_libraries/hxnodejs.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/HaxeFoundation/hxnodejs#d2d871c5b4d4589fa4ccb6e2031f982e172f8860" into hxnodejs/12.1.0/github/d2d871c5b4d4589fa4ccb6e2031f982e172f8860 2 | -cp ${HAXE_LIBCACHE}/hxnodejs/12.1.0/github/d2d871c5b4d4589fa4ccb6e2031f982e172f8860/src 3 | -D hxnodejs=12.1.0 4 | --macro allowPackage('sys') 5 | # should behave like other target defines and not be defined in macro context 6 | --macro define('nodejs') 7 | --macro _internal.SuppressDeprecated.run() 8 | -------------------------------------------------------------------------------- /openlife/macros/Debug.hx: -------------------------------------------------------------------------------- 1 | package openlife.macros; 2 | 3 | import openlife.settings.OpenLifeData; 4 | import sys.FileSystem; 5 | 6 | class Debug { 7 | #if macro 8 | public static function run() { 9 | if (!FileSystem.exists("data.json")) return; 10 | var data = OpenLifeData.getData(); 11 | if (data.debug) { 12 | haxe.macro.Compiler.define("debug", ""); 13 | // haxe.macro.Compiler.addGlobalMetadata("--no-inline"); 14 | // haxe.macro.Compiler.addNativeArg("-v"); 15 | } 16 | } 17 | #end 18 | } 19 | -------------------------------------------------------------------------------- /openlife/data/animation/SoundParameter.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation; 2 | 3 | import openlife.data.sound.SoundData; 4 | import haxe.ds.Vector; 5 | 6 | @:expose 7 | class SoundParameter { 8 | public var sounds:Vector; 9 | 10 | public var repeatPerSec:Float = 0; 11 | public var repeatPhase:Float = 0; 12 | 13 | public var ageStart:Float; 14 | public var ageEnd:Float; 15 | 16 | /** 17 | * default footstep sounds are replaced with floor usage 18 | */ 19 | var footstep:String = ""; 20 | 21 | public function new() {} 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "haxe", 6 | "args": "active configuration", 7 | "problemMatcher": [ 8 | "$haxe-absolute", 9 | "$haxe", 10 | "$haxe-error", 11 | "$haxe-trace" 12 | ], 13 | "group": "build", 14 | "label": "haxe: active configuration" 15 | }, 16 | { 17 | "type": "hxml", 18 | "file": "app.hxml", 19 | "problemMatcher": [ 20 | "$haxe-absolute", 21 | "$haxe", 22 | "$haxe-error", 23 | "$haxe-trace" 24 | ], 25 | "group": "build", 26 | "label": "haxe: app.hxml" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /openlife/data/sound/SoundData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.sound; 2 | 3 | @:expose 4 | class SoundData { 5 | /** 6 | * id of the sound 7 | */ 8 | public var id:Int = 0; 9 | 10 | /** 11 | * multiplyer of the volume 12 | */ 13 | public var multi:Float = 0; 14 | 15 | /** 16 | * whether music or sound 17 | */ 18 | public var music:Bool = false; 19 | 20 | /** 21 | * New sound data generate 22 | * @param string "id:multi" 23 | */ 24 | public function new(string:String) { 25 | var array = string.split(":"); 26 | id = Std.parseInt(array[0]); 27 | multi = Std.parseFloat(array[1]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest,windows-latest] 10 | node-version: [12.x] 11 | fail-fast: true 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: npm install 20 | - run: npm run build --if-present 21 | - run: npm test 22 | env: 23 | CI: true 24 | -------------------------------------------------------------------------------- /scripts/HLDebug.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import sys.io.File; 4 | import sys.FileSystem; 5 | 6 | class HLDebug { 7 | public static function main() { 8 | for (lib in ["vshaxe", "vscode", "vscode-debugadapter"]) { 9 | Sys.command('haxelib install $lib'); 10 | } 11 | if (!FileSystem.exists("hashlink-debugger")) { 12 | Sys.command("git clone https://github.com/vshaxe/hashlink-debugger"); 13 | } 14 | final path = "hashlink-debugger/debugger/"; 15 | final init = Sys.getCwd(); 16 | trace("init " + init); 17 | Sys.setCwd(path); 18 | trace("set"); 19 | Sys.command('haxe debugger.hxml'); 20 | Sys.setCwd(init); 21 | File.copy(path + "debug.hl", "debug.hl"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /objectDescTags.txt: -------------------------------------------------------------------------------- 1 | +tool 2 | +toolPlane 3 | +toolSew 4 | +toolBowDrill 5 | +toolHoe 6 | +autoDefaultTrans 7 | - Jungle Only# +biomeReq6 8 | +biomeBlock 9 | +useOnContained 10 | +neverDrop 11 | +causeAutoOrientH" 12 | +noBackAccess 13 | +wall 14 | +primaryHomeland 15 | +vertialDoorSnow 16 | +noWait 17 | +cont 18 | +horizontalD 19 | +horizontalE 20 | +cornerD 21 | +verticalD 22 | +verticalE 23 | +causeAutoOrientH 24 | +owned 25 | +causeAutoOrient 26 | +hungryWork10 27 | +noWait 28 | +tapoutTrigger,1,1,2,1 29 | +land 30 | +fly 31 | +yum 32 | +yumID 33 | +frontWall 34 | +drink 35 | &writable 36 | +map 37 | maxPickupAge_[x] 38 | +noHighlight 39 | +default 40 | +becomeUseX 41 | +noCover 42 | +normalOnly -------------------------------------------------------------------------------- /openlife/auto/Overseer.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import ClientAi.Bot; 4 | 5 | class Overseer { 6 | public function new() {} 7 | 8 | public function run(bots:Array) { 9 | // Do all the calculations for resources and items and etc and then check which bots are available for which roles 10 | // Assign roles 11 | 12 | // OR we can have a foreach here and do some calculation/assignments in the same loop where we call bot.update 13 | // To have a case by case kinda basis. 14 | // This would reduce processing at the cost of precision. 15 | 16 | // Run the bots 17 | for (bot in bots) 18 | bot.update(); 19 | // for (ai in ais) 20 | // bot.update(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /openlife/settings/OpenLifeData.hx: -------------------------------------------------------------------------------- 1 | package openlife.settings; 2 | 3 | import sys.io.File; 4 | import haxe.Json; 5 | import sys.FileSystem; 6 | import sys.io.File; 7 | 8 | class OpenLifeData { 9 | public static function getData():OpenLifeDataType { 10 | if (!FileSystem.exists("data.json")) return getDefault(); 11 | var data:OpenLifeDataType = cast Json.parse(File.getContent("data.json")); 12 | return data; 13 | } 14 | 15 | public static function getDefault():OpenLifeDataType { 16 | return { 17 | relay: true, 18 | combo: 0, 19 | syncSettings: true, 20 | script: "Script.hx", 21 | debug: false 22 | }; 23 | } 24 | } 25 | 26 | typedef OpenLifeDataType = {relay:Bool, combo:Int, syncSettings:Bool, script:String, debug:Bool} 27 | -------------------------------------------------------------------------------- /openlife/auto/actions/Eat.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.auto.Action; 4 | 5 | class Eat extends openlife.auto.Action { 6 | public function new() { 7 | this.name = 'Eat'; 8 | } 9 | 10 | ///As long as this function returns true we continue the action 11 | // When this function returns false we call nextAction from the role 12 | override public function isValidAction():Bool { 13 | return true; 14 | } 15 | 16 | override public function step(bot:BotType) { 17 | // call this.work(); 18 | if (this.isValidTarget()) { // this isValidTarget() check is subject to change 19 | this.work(bot); 20 | } 21 | } 22 | 23 | override public function work(bot:BotType) { 24 | bot.program.self(); 25 | return; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/launch.json 2 | /yarn.lock 3 | bin 4 | cred.json 5 | modules 6 | extension 7 | export 8 | cred 9 | cred2 10 | cred3 11 | cred4 12 | dummymap 13 | terminal.n 14 | output.n 15 | login.txt 16 | assets/data 17 | extensions 18 | .DS_Store 19 | dir 20 | mf 21 | config.json 22 | data.json 23 | serverDesign.md 24 | father.txt 25 | publish.n 26 | OneLifeData7 27 | TwoLifeData7 28 | EMOTEPROTOCOL.md 29 | OpenLifeModTextures 30 | OneLifeGameSourceData 31 | src/Secret.hx 32 | food_efficency.txt 33 | temp 34 | pages 35 | docs 36 | dox 37 | *.n 38 | *.js 39 | *.hl 40 | !server.hl 41 | /*.hx 42 | xml 43 | dir 44 | *.d.ts 45 | .idea 46 | *.exe 47 | *.ts 48 | *.png 49 | combo.txt 50 | node_modules 51 | package-lock.json 52 | hashlink-debugger 53 | *.db 54 | /SaveFiles -------------------------------------------------------------------------------- /openlife/data/object/ObjectKey.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.object; 2 | 3 | @:expose 4 | @:enum abstract ObjectKey(Null) { 5 | public var CLOTHING = "clothing"; 6 | public var FOOD = "food"; 7 | public var TOOL = "tools"; 8 | public var CONTAINER = "container"; 9 | public var HEAT_SOURCE = "heat"; 10 | public var WATER_SOURCE = "water"; 11 | public var NATURAL = "natural"; 12 | 13 | @:from private static function fromString(value:String):ObjectKey { 14 | return switch (value) { 15 | case "clothing": CLOTHING; 16 | case "food": FOOD; 17 | case "tools": TOOL; 18 | case "container": CONTAINER; 19 | case "heat": HEAT_SOURCE; 20 | case "water": WATER_SOURCE; 21 | case "natural": NATURAL; 22 | default: null; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /openlife/auto/Ai.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import haxe.Exception; 4 | import openlife.data.map.MapData; 5 | import openlife.data.object.ObjectData; 6 | import openlife.data.object.ObjectHelper; 7 | import openlife.data.object.player.PlayerInstance; 8 | import openlife.data.transition.TransitionData; 9 | import openlife.data.transition.TransitionImporter; 10 | import openlife.macros.Macro; 11 | import openlife.server.Connection; 12 | import openlife.server.GlobalPlayerInstance; 13 | import openlife.server.NamingHelper; 14 | import openlife.server.TimeHelper; 15 | import openlife.server.WorldMap; 16 | import openlife.settings.ServerSettings; 17 | import sys.thread.Thread; 18 | 19 | using StringTools; 20 | using openlife.auto.AiHelper; 21 | 22 | class Ai extends AiBase {} 23 | -------------------------------------------------------------------------------- /openlife/data/map/MapInstance.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.map; 2 | 3 | /** 4 | * Map chunk 5 | */ 6 | @:expose("MapInstance") 7 | class MapInstance { 8 | /** 9 | * Tile X 10 | */ 11 | public var x:Int = 0; 12 | 13 | /** 14 | * Tile Y 15 | */ 16 | public var y:Int = 0; 17 | 18 | /** 19 | * Tile Width 20 | */ 21 | public var width:Int = 0; 22 | 23 | /** 24 | * Tile Height 25 | */ 26 | public var height:Int = 0; 27 | 28 | /** 29 | * New map chunk created 30 | */ 31 | public function new() {} 32 | 33 | /** 34 | * String for debug "pos(x,y) size(width,height) raw: rawSizeInt compress: compressSizeInt" 35 | * @return String 36 | */ 37 | public function toString():String { 38 | return "pos(" + x + "," + y + ") size(" + width + "," + height + ")"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /openlife/auto/actions/Take.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.auto.Action; 4 | 5 | class Take extends openlife.auto.Action { 6 | public function new() { 7 | this.name = 'Take'; 8 | } 9 | 10 | // As long as this function returns true we continue the action 11 | // When this function returns false we call nextAction from the role 12 | override public function isValidAction():Bool { 13 | return true; 14 | } 15 | 16 | override public function step(bot:BotType) { 17 | // check isValidAction and other validators if present 18 | if (this.isValidAction()) { // this isValidAction() check is subject to change 19 | this.work(bot); 20 | } 21 | // then call this.work(); 22 | } 23 | 24 | override public function work(bot:BotType) { 25 | // Take target object 26 | bot.program.use(bot.target.x, bot.target.y); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /openlife/auto/actions/Travel.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.auto.Action; 4 | 5 | class Travel extends openlife.auto.Action { 6 | public function new() { 7 | this.name = 'Travel'; 8 | } 9 | 10 | // As long as this function returns true we continue the action 11 | // When this function returns false we call nextAction from the role 12 | override public function isValidAction():Bool { 13 | return true; 14 | } 15 | 16 | override public function step(bot:BotType) { 17 | // check isValidAction and other validators if present 18 | if (this.isValidAction()) { // this isValidAction() check is subject to change 19 | this.work(bot); 20 | } 21 | // then call this.work(); 22 | } 23 | 24 | override public function work(bot:BotType) { 25 | // move towards target 26 | bot.moveTo(bot.target.x, bot.target.y); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /openlife/resources/Emote.hx: -------------------------------------------------------------------------------- 1 | package openlife.resources; 2 | 3 | import haxe.ds.Vector; 4 | import openlife.data.animation.emote.EmoteData; 5 | 6 | @:expose 7 | class Emote { 8 | /** 9 | * Visual generate emote data 10 | */ 11 | public static function get(settings:openlife.settings.Settings):Vector { 12 | if (!settings.data.exists("emotionObjects") || settings.data.exists("emotionWords")) { 13 | trace("no emote data in settings"); 14 | return null; 15 | } 16 | var arrayObj:Array = settings.data.get("emotionObjects").split("\n"); 17 | var arrayWord:Array = settings.data.get("emotionWords").split("\n"); 18 | var emotes = new Vector(arrayObj.length); 19 | for (i in 0...arrayObj.length) 20 | emotes[i] = new openlife.data.animation.emote.EmoteData(arrayWord[i], arrayObj[i]); 21 | return emotes; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openlife", 3 | "version": "0.0.9", 4 | "description": "A haxe based OneLife engine", 5 | "directories": { 6 | "test": "tests" 7 | }, 8 | "scripts": { 9 | "test": "echo \"Error: no test specified\"", 10 | "postinstall": "lix download" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/pxshadow/openlife.git" 15 | }, 16 | "keywords": [ 17 | "onelife", 18 | "haxe", 19 | "engine", 20 | "module" 21 | ], 22 | "author": "PXshadow", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/pxshadow/openlife/issues" 26 | }, 27 | "homepage": "https://github.com/pxshadow/openlife#readme", 28 | "devDependencies": { 29 | "lix": "^15.9.1" 30 | }, 31 | "files": [ 32 | "index.js", 33 | "index.d.ts" 34 | ], 35 | "dependencies": { 36 | "deasync": "^0.1.20" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /BUILDING.MD: -------------------------------------------------------------------------------- 1 | Build OpenLife 2 | ============= 3 | 0. [Download Git](https://git-scm.com/downloads) 4 | 1. [Download NodeJS+NPM](https://nodejs.org/en/download/) 5 | 2. [Download Hashlink](https://hashlink.haxe.org/#download) (add it to your system PATH) on mac you can do ```brew install hashlink``` on linux you have to build it yourself with make 6 | 3. terminal: ```npm install``` 7 | 4. download OneLifeData, terminal: ```npx haxe setup_data_client.hxml``` 8 | 5. build app, terminal: ```npx haxe app.hxml``` 9 | 6. run app, terminal: ```hl app.hl``` 10 | 7. first time run it will generate config.json and data.json, these are gitignore files and can be used to set how the app behaves 11 | 12 | ## Extra Notes 13 | 1. ```lix download``` to download all of the libraries that are added in this project 14 | 2. to install a new library do ```lix install github_url``` and it will install the library and be added to haxe_libraries folder for example ```lix install https://github.com/haxefoundation/format``` -------------------------------------------------------------------------------- /openlife/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "HashLink (launch)", 9 | "request": "launch", 10 | "type": "hl", 11 | "cwd": "${workspaceFolder}", 12 | "preLaunchTask": { 13 | "type": "haxe", 14 | "args": "active configuration" 15 | } 16 | }, 17 | { 18 | "name": "HashLink (attach)", 19 | "request": "attach", 20 | "port": 6112, 21 | "type": "hl", 22 | "cwd": "${workspaceFolder}", 23 | "preLaunchTask": { 24 | "type": "haxe", 25 | "args": "active configuration" 26 | } 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 PXshadow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/Bake.hx: -------------------------------------------------------------------------------- 1 | import openlife.engine.Engine; 2 | import openlife.data.object.ObjectData; 3 | import sys.io.File; 4 | import openlife.resources.ObjectBake; 5 | import sys.FileSystem; 6 | import haxe.Serializer; 7 | import haxe.Unserializer; 8 | import haxe.ds.Vector; 9 | 10 | class Bake { 11 | public static function run():Vector { 12 | var vector = ObjectBake.objectList(); 13 | if (FileSystem.exists(Engine.dir + "dummymap") && ObjectBake.baked) { 14 | ObjectBake.dummies = cast Unserializer.run(File.getContent(Engine.dir + "dummymap")); 15 | return vector; 16 | } 17 | var list = ObjectBake.objectData(vector); 18 | var index = ObjectBake.nextObjectNumber; 19 | var i:Int = 0; 20 | var p:Float = 0; 21 | var last:Float = -0.05; 22 | for (obj in list) { 23 | obj.id = ++index; 24 | ObjectBake.dummy(obj); 25 | p = ++i / list.length; 26 | if (last + 0.1 < p) { 27 | trace("baking " + Std.int(p * 100) + "%"); 28 | Sys.sleep(0.01); 29 | last = p; 30 | } 31 | } 32 | File.saveContent(Engine.dir + "dummymap", Serializer.run(ObjectBake.dummies)); 33 | ObjectBake.finish(); 34 | return vector; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /openlife/data/object/player/PlayerMove.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.object.player; 2 | 3 | @:expose("PlayerMove") 4 | class PlayerMove { 5 | public var id:Int = 0; 6 | public var x:Int = 0; 7 | public var y:Int = 0; 8 | public var endX:Int = 0; 9 | public var endY:Int = 0; 10 | public var total:Float = 0; 11 | public var eta:Float = 0; 12 | public var trunc:Bool = false; 13 | public var moves:Array = []; 14 | 15 | var movesString:String; 16 | 17 | public function new(a:Array) { 18 | var i:Int = 0; 19 | id = Std.parseInt(a[i++]); 20 | x = Std.parseInt(a[i++]); 21 | y = Std.parseInt(a[i++]); 22 | total = Std.parseFloat(a[i++]); 23 | eta = Std.parseFloat(a[i++]); 24 | trunc = a[i++] == "1"; 25 | a = a.splice(i, a.length); 26 | for (i in 0...Std.int(a.length / 2)) { 27 | moves.push(new Pos(Std.parseInt(a[i * 2]), Std.parseInt(a[i * 2 + 1]))); 28 | } 29 | movesString = a.join(" "); 30 | var pos = moves.pop(); 31 | endX = x + pos.x; 32 | endY = y + pos.y; 33 | moves.push(pos); 34 | } 35 | 36 | public function toData():String { 37 | return '$id $x $y $total $eta ${trunc ? 1 : 0} $movesString'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /openlife/data/animation/emote/EmoteData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation.emote; 2 | 3 | @:expose 4 | class EmoteData { 5 | /** 6 | * Word to trigger the emote 7 | */ 8 | public var triggerWord:String = ""; 9 | 10 | /** 11 | * Eye index 12 | */ 13 | public var eyeEmot:Int = 0; 14 | 15 | /** 16 | * Mouth index 17 | */ 18 | public var mouthEmot:Int = 0; 19 | 20 | /** 21 | * Other index 22 | */ 23 | public var otherEmot:Int = 0; 24 | 25 | /** 26 | * Face index 27 | */ 28 | public var faceEmot:Int = 0; 29 | 30 | /** 31 | * Body index 32 | */ 33 | public var bodyEmot:Int = 0; 34 | 35 | /** 36 | * Head index 37 | */ 38 | public var headEmot:Int = 0; 39 | 40 | /** 41 | * Create new emote 42 | * @param word trigger 43 | * @param string data buffer 44 | */ 45 | public function new(word:String, string:String) { 46 | triggerWord = word; 47 | var array = string.split(" "); 48 | eyeEmot = Std.parseInt(array[0]); 49 | mouthEmot = Std.parseInt(array[1]); 50 | otherEmot = Std.parseInt(array[2]); 51 | faceEmot = Std.parseInt(array[3]); 52 | bodyEmot = Std.parseInt(array[4]); 53 | headEmot = Std.parseInt(array[5]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /openlife/auto/actions/Search.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.actions; 2 | 3 | import openlife.data.Target; 4 | import openlife.auto.Action; 5 | 6 | class Search extends openlife.auto.Action { 7 | public function new() { 8 | this.name = 'Search'; 9 | } 10 | 11 | // As long as this function returns true we continue the action 12 | // When this function returns false we call nextAction from the role 13 | override public function isValidAction():Bool { 14 | return true; 15 | } 16 | 17 | // We are going to use newTarget() to check inside work() if 18 | // we need to keep going or not. 19 | // once a target has been discovered and added to the bot 20 | // we can return true from work and continue to the next action 21 | // target id should be type IDs, not unique IDs 22 | // check discovered map for target id 23 | // if target id not present continue spiral search 24 | override public function newTarget(bot:BotType):Bool { 25 | return false; 26 | } 27 | 28 | override public function step(bot:BotType) { 29 | // call this.work(); 30 | } 31 | 32 | // spiral search needs to use Bot.moveTo() or Bot.goTo() (whatever we name it) 33 | override public function work(bot:BotType) { 34 | // Do nothing 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /openlife/data/ArrayDataInt.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | /** 4 | * Int version of ArrayDataArray 5 | */ 6 | class ArrayDataInt { 7 | var array:Array> = []; 8 | 9 | // diffrence 10 | public var dx:Int = 0; 11 | public var dy:Int = 0; 12 | 13 | public function new() { 14 | array[0] = []; 15 | } 16 | 17 | public function clear() { 18 | array = []; 19 | dx = 0; 20 | dy = 0; 21 | } 22 | 23 | public function row(y:Int):Array { 24 | return array[y - dy]; 25 | } 26 | 27 | public function get(x:Int, y:Int):Int { 28 | if (array[y - dy] != null) { 29 | return array[y - dy][x - dx]; 30 | } 31 | return 0; 32 | } 33 | 34 | public function shiftY(y:Int) { 35 | // shift 36 | if (y < dy) { 37 | for (i in 0...dy - y) 38 | array.unshift([]); 39 | dy = y; 40 | } 41 | } 42 | 43 | public function shiftX(x:Int, value:Int) { 44 | if (x < dx) { 45 | for (j in 0...array.length) { 46 | if (array[j] == null) array[j] = []; 47 | for (i in 0...dx - x) { 48 | array[j].unshift(0); 49 | } 50 | } 51 | dx = x; 52 | } 53 | } 54 | 55 | public function set(x:Int, y:Int, value:Int) { 56 | shiftY(y); 57 | shiftX(x, value); 58 | // set value 59 | if (array[y - dy] == null) array[y - dy] = []; 60 | array[y - dy][x - dx] = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /openlife/data/ArrayData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | /** 4 | * Int version of ArrayDataArray 5 | */ 6 | @:generic class ArrayData { 7 | var array:Array> = []; 8 | 9 | // diffrence 10 | public var dx:Int = 0; 11 | public var dy:Int = 0; 12 | 13 | public function new() { 14 | array[0] = []; 15 | } 16 | 17 | public function clear() { 18 | array = []; 19 | dx = 0; 20 | dy = 0; 21 | } 22 | 23 | public function row(y:Int):Array { 24 | return array[y - dy]; 25 | } 26 | 27 | public function get(x:Int, y:Int):T { 28 | if (array[y - dy] != null) { 29 | return array[y - dy][x - dx]; 30 | } 31 | return null; 32 | } 33 | 34 | public function shiftY(y:Int) { 35 | // shift 36 | if (y < dy) { 37 | for (i in 0...dy - y) 38 | array.unshift([]); 39 | dy = y; 40 | } 41 | } 42 | 43 | public function shiftX(x:Int, value:T) { 44 | if (x < dx) { 45 | for (j in 0...array.length) { 46 | if (array[j] == null) array[j] = []; 47 | for (i in 0...dx - x) { 48 | array[j].unshift(null); 49 | } 50 | } 51 | dx = x; 52 | } 53 | } 54 | 55 | public function set(x:Int, y:Int, value:T) { 56 | shiftY(y); 57 | shiftX(x, value); 58 | // set value 59 | if (array[y - dy] == null) array[y - dy] = []; 60 | array[y - dy][x - dx] = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /openlife/data/transition/Category.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.transition; 2 | 3 | @:expose 4 | class Category { 5 | public var ids:Array; 6 | public var weights:Array; 7 | public var parentID:Int = 0; 8 | public var pattern:Bool = false; 9 | public var probSet:Bool = false; 10 | 11 | public function new(text:String) { 12 | ids = []; 13 | weights = []; 14 | var lines = text.split("\n"); 15 | var headers:Bool = true; 16 | for (line in lines) { 17 | headers ? headers = processHeader(line) : processObject(line); 18 | } 19 | } 20 | 21 | private function processHeader(line:String) { 22 | var parts = line.split("="); 23 | switch (StringTools.replace(parts[0], "\r", "")) { 24 | case "parentID": 25 | parentID = Std.parseInt(parts[1]); 26 | case "pattern": 27 | pattern = true; 28 | case "probSet": 29 | probSet = true; 30 | case "numObjects": 31 | return false; 32 | default: 33 | trace('Unknown category header |${parts[0]}|'); 34 | } 35 | return true; 36 | } 37 | 38 | private function processObject(line:String) { 39 | var parts = line.split(" "); 40 | ids.push(Std.parseInt(parts[0])); 41 | if (probSet) weights.push(Std.parseFloat(parts[1])); 42 | } 43 | 44 | public function toString():String { 45 | return 'parent id: $parentID is pattern: $pattern prob: $probSet ids: $ids'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /scripts/FileTools.hx: -------------------------------------------------------------------------------- 1 | package scripts; 2 | 3 | import haxe.io.Path; 4 | import sys.FileSystem; 5 | import sys.io.File; 6 | 7 | class FileTools { 8 | public static function copyDir(path:String, newpath:String) { 9 | path = Path.addTrailingSlash(path); 10 | newpath = Path.addTrailingSlash(newpath); 11 | if (FileSystem.exists(path) && FileSystem.isDirectory(path)) { 12 | if (!FileSystem.exists(newpath)) FileSystem.createDirectory(newpath); 13 | var dir = FileSystem.readDirectory(path); 14 | for (name in dir) { 15 | if (name.substring(0, 1) == ".") continue; // skip git 16 | 17 | if (FileSystem.isDirectory(path + name)) { 18 | FileSystem.createDirectory(newpath + name); 19 | } else { 20 | File.copy(path + name, newpath + name); 21 | } 22 | } 23 | } 24 | } 25 | 26 | public static function deleteDir(path:String) { 27 | path = Path.addTrailingSlash(path); 28 | if (FileSystem.exists(path) && FileSystem.isDirectory(path)) { 29 | var dir = FileSystem.readDirectory(path); 30 | var i:Int = 0; 31 | for (name in dir) { 32 | // if (name.substring(0,1) == ".") continue; //skip git 33 | 34 | if (FileSystem.isDirectory(path + name)) { 35 | deleteDir(path + name); 36 | sys.FileSystem.deleteDirectory(path + name); 37 | } else { 38 | FileSystem.deleteFile(path + name); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /openlife/data/animation/AnimationRecord.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation; 2 | 3 | import haxe.ds.Vector; 4 | 5 | @:expose 6 | class AnimationRecord { 7 | /** 8 | * Record id 9 | */ 10 | public var id:Int = -1; 11 | 12 | /** 13 | * Record type 14 | */ 15 | public var type:AnimationType; 16 | 17 | /** 18 | * Parameters of record 19 | */ 20 | public var params:Vector; 21 | 22 | /** 23 | * Number of sounds 24 | */ 25 | public var numSounds:Int = 0; 26 | 27 | /** 28 | * Number of sprites 29 | */ 30 | public var numSprites:Int = 0; 31 | 32 | /** 33 | * Number of slots 34 | */ 35 | public var numSlots:Int = 0; 36 | 37 | /** 38 | * Random start phase 39 | */ 40 | public var randStartPhase:Float = 0; 41 | 42 | /** 43 | * N/A 44 | */ 45 | public var forceZeroStart:Float = 0; 46 | 47 | /** 48 | * Sound record 49 | */ 50 | public var soundAnim:Vector; 51 | 52 | /** 53 | * Slot animation 54 | */ 55 | public var slotAnim:Vector; 56 | 57 | public function new() {} 58 | 59 | /** 60 | * String to show fields and properties 61 | * @return String 62 | */ 63 | public function toString():String { 64 | var string:String = ""; 65 | for (field in Reflect.fields(this)) { 66 | string += field + ": " + Reflect.getProperty(this, field) + "\n"; 67 | } 68 | return string; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /openlife/auto/Interpreter.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.data.object.ObjectData; 4 | import haxe.ds.Vector; 5 | 6 | @:expose 7 | class Interpreter { 8 | public function new() {} 9 | 10 | public function stringNumber(string:String):Int { 11 | return switch (string) { 12 | case "couple": 2; 13 | case "many": 6; 14 | case "few": 3; 15 | case "one": 1; 16 | case "two": 2; 17 | case "three": 3; 18 | case "four": 4; 19 | case "five": 5; 20 | case "six": 6; 21 | case "seven": 7; 22 | case "eight": 8; 23 | case "nine": 9; 24 | case "ten": 10; 25 | case "eleven": 11; 26 | case "twevle": 12; 27 | case "thirteen": 13; 28 | case "fourteen": 14; 29 | case "fiveteen": 15; 30 | case "cart" | "cartfull": 4; 31 | case "basket" | "basketfull": 3; 32 | default: 1; 33 | } 34 | } 35 | 36 | /*public function stringObject(words:Array):Int 37 | { 38 | for (id in list) 39 | { 40 | var desc = new ObjectData(id,true).description.toUpperCase(); 41 | var fail:Bool = false; 42 | for (word in words) 43 | { 44 | fail = desc.indexOf(word) == -1; 45 | if (fail) break; 46 | } 47 | if (!fail) return id; 48 | } 49 | return -1; 50 | }*/ 51 | private inline function removePlural(string:String):String { 52 | if (string.substring(string.length - 1, string.length) == "s") string = string.substring(0, string.length - 1); 53 | return string; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /openlife/auto/Action.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.engine.Engine; 4 | 5 | class Action { 6 | public var name:String = ""; 7 | public var maxPerTarget:Int = 99; 8 | public var maxPerAction:Int = 99; 9 | public var targetRange:Int = 1; 10 | public var reachedRange:Int = 1; 11 | public var eventOverride = function() {}; 12 | public var eventOverrideBool:Bool = false; 13 | 14 | // Might want to add a field inside action to indicate when the action is completed 15 | // used to check whether the action can still be performed or not 16 | public function isValidAction():Bool { 17 | return true; 18 | } 19 | 20 | // used for travelling and error checking 21 | public function isValidTarget():Bool { 22 | return true; 23 | } 24 | 25 | // TODO: target finding code should go in here so that it can be reused across actions 26 | // used for travelling and error checking 27 | public function isAddableTarget():Bool { 28 | return true; 29 | } 30 | 31 | // used to find a new target in case of failure 32 | public function newTarget(bot:BotType):Bool { 33 | return true; 34 | } 35 | 36 | public function step(bot:BotType) { 37 | // this is where the action gets run 38 | if (eventOverrideBool == true) { 39 | eventOverride(); 40 | return; 41 | } 42 | } 43 | 44 | public function work(bot:BotType) { 45 | // gets called from step once we are at target 46 | } 47 | 48 | public function assign(bot:BotType) { 49 | // Here we set the bot's action in memory. Bot.hx will have an action field. 50 | bot.currentAction = this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /openlife/data/map/MapChange.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.map; 2 | 3 | @:expose("MapChange") 4 | class MapChange { 5 | /** 6 | * Tile X 7 | */ 8 | public var x:Int = 0; 9 | 10 | /** 11 | * Tile Y 12 | */ 13 | public var y:Int = 0; 14 | 15 | /** 16 | * Floor boolean 17 | */ 18 | public var floor:Int = 0; 19 | 20 | /** 21 | * Id array 22 | */ 23 | public var id:Array = []; 24 | 25 | /** 26 | * Player id that did the map change 27 | */ 28 | public var pid:Int = 0; 29 | 30 | /** 31 | * Old position x 32 | */ 33 | public var oldX:Int = 0; 34 | 35 | /** 36 | * Old position y 37 | */ 38 | public var oldY:Int = 0; 39 | 40 | /** 41 | * Speed of changed object 42 | */ 43 | public var speed:Float = 0; 44 | 45 | /** 46 | * New map change data 47 | * @param array properties of map change 48 | */ 49 | public function new(array:Array) { 50 | x = Std.parseInt(array[0]); 51 | y = Std.parseInt(array[1]); 52 | floor = Std.parseInt(array[2]); 53 | id = MapData.id(array[3]); 54 | if (id.length == 0) id = [0]; 55 | pid = Std.parseInt(array[4]); 56 | // optional speed 57 | if (array.length > 5) { 58 | oldX = Std.parseInt(array[5]); 59 | oldY = Std.parseInt(array[6]); 60 | speed = Std.parseFloat(array[7]); 61 | } 62 | } 63 | 64 | /** 65 | * string for debug 66 | * @return String 67 | */ 68 | public function toString():String { 69 | var string:String = ""; 70 | for (field in Reflect.fields(this)) { 71 | string += field + ": " + Reflect.getProperty(this, field) + "\n"; 72 | } 73 | return string; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /openlife/client/Game.hx: -------------------------------------------------------------------------------- 1 | import h2d.Graphics; 2 | import hxd.snd.ChannelGroup; 3 | import openlife.data.object.ObjectData; 4 | import openlife.engine.Engine; 5 | import openlife.engine.Utility; 6 | import sys.FileSystem; 7 | 8 | var s2d:h2d.Scene = null; 9 | var engineHeaps:h3d.Engine = null; 10 | var sevents:hxd.SceneEvents = null; 11 | var engine:Engine = null; 12 | var world:World = null; 13 | var soundChannel:ChannelGroup; 14 | var g:Graphics; 15 | 16 | function init() { 17 | Engine.dir = Utility.dir(); 18 | if (!FileSystem.exists(ClientSettings.SaveDirectory)) FileSystem.createDirectory(ClientSettings.SaveDirectory); 19 | soundChannel = new ChannelGroup("master"); 20 | engineHeaps.backgroundColor = 0x2b2932; 21 | world = new World(); 22 | engine = new Engine(world, null, null, "OneLifeData7/"); 23 | ObjectData.DoAllTheObjectInititalisationStuff(false); 24 | Render.loadGround(); // seralized 25 | Render.loadSprites(); // serialized 26 | g = new Graphics(s2d); 27 | // Render.addObject(3, 3, [19]); 28 | // Render.addObject(3, 4, [575]); 29 | /*for (x in 0...7) { 30 | for (y in 0...7) { 31 | Render.addGround(2, x, y); 32 | } 33 | }*/ 34 | 35 | new Fps(150, s2d); 36 | engine.client.config = {ip: "localhost", port: 8005}; 37 | engine.connect(); 38 | } 39 | 40 | function update(dt:Float) { 41 | engine.client.update(); 42 | Render.update(dt); 43 | /*g.clear(); 44 | g.lineStyle(1, 0xFFFFFF); 45 | for (obj in Render.objs) { 46 | obj.updateBounds(); 47 | g.drawRect(obj.bounds.x, obj.bounds.y, obj.bounds.width, obj.bounds.height); 48 | }*/ 49 | } 50 | 51 | function resize() {} 52 | -------------------------------------------------------------------------------- /openlife/auto/roles/BerryEater.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto.roles; 2 | 3 | import haxe.iterators.StringIteratorUnicode; 4 | import openlife.auto.Role; 5 | import openlife.auto.actions.*; 6 | 7 | class BerryEater extends openlife.auto.Role { 8 | public function new() { 9 | this.name = 'BerryEater'; 10 | // find closest berry with map functions or overseer functions 11 | this.actions = [new Search(), new Travel(), new Take(), new Eat()]; 12 | } 13 | 14 | override public function selectAction():Action { 15 | // still working on this 16 | // need to think about how to check the action validity and select the action 17 | // role needs to call action.newTarget() 18 | 19 | // super.selectAction(); 20 | 21 | for (a in actions) { 22 | if (a.isValidAction()) { 23 | // a.assign(bot.role); 24 | // once the action is assigned it should get automatically called by role.run() 25 | // The overseer calls role.run for each bot and assigns roles. 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | override public function run(bot:BotType) { 32 | // Check current action isValidAction 33 | // if action is valid then run action 34 | // otherwise next action 35 | 36 | // Kept this for possible overrides 37 | /* 38 | bot.event.says = function(id:Int,text:String,curse:Bool) 39 | { 40 | trace("text " + text); 41 | if (text.indexOf("BERRY") != -1) 42 | { 43 | 44 | } 45 | } 46 | bot.event.foodChange = function(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int) 47 | { 48 | if (store > 4) return; 49 | 50 | } 51 | */ 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scripts/Generate.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import sys.FileSystem; 4 | import haxe.io.Path; 5 | 6 | class Generate { 7 | public static function main() { 8 | trace("start generation"); 9 | // docs 10 | var exclude:Array = [ 11 | // haxe 12 | "haxe", 13 | "Array", 14 | "ArrayAccess", 15 | "Bool", 16 | "Class", 17 | "Date", 18 | "Dynamic", 19 | "EReg", 20 | "Enum", 21 | "EnumValue", 22 | "Float", 23 | "Int", 24 | "IntIterator", 25 | "Iterable", 26 | "Iterator", 27 | "KeyValueIterable", 28 | "KeyValueIterator", 29 | "Lambda", 30 | "Map", 31 | "Math", 32 | "Null", 33 | "Reflect", 34 | "Std", 35 | "String", 36 | "StringBuf", 37 | "StringTools", 38 | "Sys", 39 | "Type", 40 | "Void", 41 | "Single", 42 | "Any", 43 | "List", 44 | "Xml", 45 | // main 46 | "Main", 47 | // "Static", 48 | "std", 49 | "shaders", 50 | // "ApplicationMain", 51 | // "DocumentClass", 52 | // "DefaultAssetLibrary", 53 | // target 54 | "neko", 55 | "sys", 56 | "cpp", 57 | "flash" 58 | ]; 59 | trace("exclude gen"); 60 | var excludeString = '"('; 61 | for (name in exclude) 62 | excludeString += name + "|"; 63 | excludeString = excludeString.substring(0, excludeString.length - 1); 64 | excludeString += ')"'; 65 | trace("generate html api"); 66 | Sys.command('haxelib run dox -i xml -o docs/api -D version "0.0.1 alpha" -D logo "https://raw.githubusercontent.com/PXshadow/OpenLife/master/logo.png" -D title "API Reference" -D source-path "https://github.com/PXshadow/openlife/tree/master/src/" --exclude ' 67 | + excludeString); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /scripts/Docs.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import sys.FileSystem; 4 | import haxe.io.Path; 5 | 6 | function main() { 7 | trace("start generation"); 8 | if (!FileSystem.exists("./docs")) Sys.command("git clone https://github.com/PXshadow/OpenLife-Docs docs"); 9 | // docs 10 | var exclude:Array = [ 11 | // haxe 12 | "haxe", 13 | "Array", 14 | "ArrayAccess", 15 | "Bool", 16 | "Class", 17 | "Date", 18 | "Dynamic", 19 | "EReg", 20 | "Enum", 21 | "EnumValue", 22 | "Float", 23 | "Int", 24 | "IntIterator", 25 | "Iterable", 26 | "Iterator", 27 | "KeyValueIterable", 28 | "KeyValueIterator", 29 | "Lambda", 30 | "Map", 31 | "Math", 32 | "Null", 33 | "Reflect", 34 | "Std", 35 | "String", 36 | "StringBuf", 37 | "StringTools", 38 | "Sys", 39 | "Type", 40 | "Void", 41 | "Single", 42 | "Any", 43 | "List", 44 | "Xml", 45 | // main 46 | "Main", 47 | // "Static", 48 | "std", 49 | "shaders", 50 | // "ApplicationMain", 51 | // "DocumentClass", 52 | // "DefaultAssetLibrary", 53 | // target 54 | "neko", 55 | "sys", 56 | "cpp", 57 | "flash", 58 | "eval", 59 | "format", 60 | ]; 61 | trace("exclude gen"); 62 | var excludeString = '"('; 63 | for (name in exclude) 64 | excludeString += name + "|"; 65 | excludeString = excludeString.substring(0, excludeString.length - 1); 66 | excludeString += ')"'; 67 | trace("generate html api"); 68 | Sys.command('haxelib run dox -i xml -o docs/api -D version "0.0.8 alpha" -D logo "https://raw.githubusercontent.com/PXshadow/OpenLife/master/logo.png" -D title "API Reference" -D source-path "https://github.com/PXshadow/openlife/tree/master/src/" --exclude ' 69 | + excludeString); 70 | } 71 | -------------------------------------------------------------------------------- /scripts/SpriteReuse.hx: -------------------------------------------------------------------------------- 1 | package scripts; 2 | 3 | import openlife.resources.ObjectBake; 4 | import openlife.engine.Utility; 5 | import openlife.data.object.ObjectData; 6 | import openlife.engine.Engine; 7 | import haxe.ds.Vector; 8 | 9 | class SpriteReuse { 10 | public static function main() { 11 | Sys.println("start"); 12 | Engine.dir = Utility.dir(); 13 | var engine = new Engine(null); 14 | var vector = ObjectBake.objectList(); 15 | var data = new Vector>(vector.length); 16 | var index:Int = 0; 17 | var object:ObjectData; 18 | for (id in vector) { 19 | if (id % 500 == 0) trace("id " + id); 20 | object = new ObjectData(id); 21 | data[index] = []; 22 | for (sprite in object.spriteArray) { 23 | data[index].push(sprite.spriteID); 24 | } 25 | index++; 26 | } 27 | // compare data 28 | trace('index is now $index'); 29 | var percent:Float = 0; 30 | var next:Int = 0; 31 | var reused:Int = 0; 32 | var used:Array = []; 33 | for (i in 0...data.length) { 34 | percent = i / data.length * 100; 35 | if (percent > next) { 36 | trace('left $percent'); 37 | next++; 38 | } 39 | for (id in data[i]) { 40 | for (j in 0...data.length) { 41 | if (j == i) continue; 42 | // trace("id " + id + " j " + (j/data.length)); 43 | for (id2 in data[j]) { 44 | if (id == id2) { 45 | reused++; 46 | if (used.indexOf(id) == -1) used.push(id); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | // 1731806 53 | trace('sprites reused $reused amount of times from another object'); 54 | // 1518 55 | trace('amount of sprites reused ${used.length}'); 56 | // percent of sprites being reused 68% 57 | // how many sprites 2239 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /openlife/macros/Macro.hx: -------------------------------------------------------------------------------- 1 | package openlife.macros; 2 | 3 | // import openlife.server.WorldMap; 4 | // import openlife.server.GlobalPlayerInstance; 5 | import haxe.macro.Expr; 6 | 7 | class Macro { 8 | public static macro function exception(expr:Expr) { 9 | return macro if (openlife.settings.ServerSettings.debug) { 10 | $expr; 11 | } else { 12 | try { 13 | $expr; 14 | } catch (e) { 15 | trace('WARNING: ' + e + '\n' + e.details()); 16 | } 17 | } 18 | } 19 | /* 20 | public static macro function doException(expr:Expr, player:GlobalPlayerInstance, targetPlayer:GlobalPlayerInstance) 21 | { 22 | var done = false; 23 | // make sure that if both players at the same time try to interact with each other it does not end up in a dead lock 24 | while(targetPlayer.mutex.tryAcquire() == false) 25 | { 26 | player.mutex.release(); 27 | 28 | Sys.sleep(WorldMap.calculateRandomFloat() / 5); 29 | 30 | player.mutex.acquire(); 31 | } 32 | 33 | Macro.exception(done = expr); 34 | 35 | // send always PU so that player wont get stuck 36 | if(done == false) 37 | { 38 | player.connection.send(PLAYER_UPDATE,[player.toData()]); 39 | player.connection.send(FRAME); 40 | } 41 | 42 | targetPlayer.mutex.release(); 43 | }*/ 44 | } 45 | // Mutex example 46 | /**if(ServerSettings.useOnePlayerMutex) GlobalPlayerInstance.AcquireMutex(); 47 | else 48 | { 49 | this.mutex.acquire(); 50 | 51 | // make sure that if both players at the same time try to interact with each other it does not end up in a dead lock 52 | while(targetPlayer.mutex.tryAcquire() == false) 53 | { 54 | this.mutex.release(); 55 | 56 | Sys.sleep(WorldMap.calculateRandomFloat() / 5); 57 | 58 | this.mutex.acquire(); 59 | } 60 | } **/ 61 | -------------------------------------------------------------------------------- /openlife/auto/WorldInterface.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import haxe.ds.Vector; 4 | import openlife.data.object.player.PlayerInstance; 5 | import openlife.data.object.ObjectData; 6 | import openlife.data.transition.TransitionData; 7 | import openlife.data.object.ObjectHelper; 8 | 9 | interface WorldInterface { 10 | public function getObjectData(id:Int):ObjectData; 11 | //** faster way of getting ObjectData wihout needing to create the object first. Use this instead getObjectHelper if you just want the ObjectData **/ 12 | public function getObjectDataAtPosition(x:Int, y:Int):ObjectData; 13 | 14 | public function getTrans(actor:ObjectHelper, target:ObjectHelper):TransitionData; 15 | public function getTransition(actorId:Int, targetId:Int, lastUseActor:Bool = false, lastUseTarget:Bool = false, maxUseTarget:Bool = false):TransitionData; 16 | public function getTransitionByNewTarget(newTargetId:Int):Array; 17 | public function getTransitionByNewActor(newActorId:Int):Array; 18 | public function getTransitionByTarget(newTargetId:Int):Array; 19 | public function getTransitionByActor(newActorId:Int):Array; 20 | 21 | public function getBiomeId(x:Int, y:Int):Int; // since map is known no need for out of range 22 | public function isBiomeBlocking(x:Int, y:Int):Bool; // since map is known no need for out of range 23 | //** returns NULL of x,y is too far away from player **/ 24 | public function getObjectId(x:Int, y:Int):Array; 25 | //** returns NULL of x,y is too far away from player / allowNull means it wont create a object helper if there is none **/ 26 | public function getObjectHelper(x:Int, y:Int, allowNull:Bool = false):ObjectHelper; 27 | //** returns -1 of x,y is too far away from player **/ 28 | public function getFloorId(x:Int, y:Int):Int; 29 | 30 | public function getClosestPlayer(maxDistance:Int, onlyHuman:Bool = false):PlayerInterface; 31 | public function getPlayerAt(x:Int, y:Int, playerId:Int):PlayerInterface; 32 | } 33 | -------------------------------------------------------------------------------- /openlife/data/object/SpriteData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.object; 2 | 3 | // gets set by objectData 4 | @:expose 5 | class SpriteData { 6 | /** 7 | * Name of sprite 8 | */ 9 | public var name:String; 10 | 11 | /** 12 | * Id of sprite 13 | */ 14 | public var spriteID:Int = 54; 15 | 16 | public var x:Float = 0; 17 | public var y:Float = 0; 18 | 19 | /** 20 | * Rotation 21 | */ 22 | public var rot:Float = 0.000000; 23 | 24 | /** 25 | * Horizontal flip 26 | */ 27 | public var hFlip:Int = 0; 28 | 29 | /** 30 | * Color Array RGB 0-1 31 | */ 32 | public var color:Array = []; // =0.952941,0.796078,0.756863 33 | 34 | /** 35 | * Age range Array -1-60 36 | */ 37 | public var ageRange:Array = []; // =-1.000000,-1.000000 38 | 39 | /** 40 | * Parent id for child 41 | */ 42 | public var parent:Int = -1; 43 | 44 | /** 45 | * Invisible when holding 46 | */ 47 | public var invisHolding:Int = -1; 48 | 49 | /** 50 | * Invisible when worn 51 | */ 52 | public var invisWorn:Int = -1; 53 | 54 | /** 55 | * Behind slots 56 | */ 57 | public var behindSlots:Int = -1; 58 | 59 | /** 60 | * Invisible when in a container 61 | */ 62 | public var invisCont:Bool = false; 63 | 64 | /** 65 | * Offset of center x 66 | */ 67 | public var inCenterXOffset:Int = 0; 68 | 69 | /** 70 | * Offset of center y 71 | */ 72 | public var inCenterYOffset:Int = 0; 73 | 74 | public function new() {} 75 | 76 | public function toString():String { 77 | return 'spriteID=$spriteID${LineReader.EOL}' 78 | + 'pos=$x,$y${LineReader.EOL}' 79 | + 'rot=$rot${LineReader.EOL}' 80 | + 'hFlip=$hFlip${LineReader.EOL}' 81 | + 'color=${color[0]},${color[1]},${color[2]}${LineReader.EOL}' 82 | + 'ageRange=${ageRange[0]},${ageRange[1]}${LineReader.EOL}' 83 | + 'parent=$parent${LineReader.EOL}' 84 | + 'invisHolding=$invisHolding,invisWorn=$invisWorn,behindSlots=$behindSlots${LineReader.EOL}' 85 | + 'invisCount=${invisCont ? "1" : "0"}${LineReader.EOL}'; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /scripts/SetupData.hx: -------------------------------------------------------------------------------- 1 | package scripts; 2 | 3 | import sys.io.Process; 4 | import sys.FileSystem; 5 | 6 | var users:Array = ["jasonrohrer", "twohoursonelife"]; 7 | var index:Null; 8 | 9 | function SetupGameData() { 10 | if (index == null || index < 0 || index > users.length - 1) index = 0; 11 | var cwd = Sys.getCwd(); 12 | // linux is folder name case senetive 13 | if (!FileSystem.exists("OneLifeData7")) { 14 | Sys.println('Rep input an index of (0) or (1) for $users :'); 15 | index = Std.parseInt(Sys.stdin().readLine()); 16 | Sys.println('Downloading ${users[index]}'); 17 | trace("clone-"); 18 | Sys.command('git clone https://github.com/${users[index]}/OneLifeData7.git'); 19 | } 20 | Sys.setCwd("OneLifeData7"); 21 | trace("pull-"); 22 | Sys.command('git fetch --force'); 23 | Sys.command("git fetch --tags"); 24 | var proc = new Process("git for-each-ref --sort=-creatordate --format '%(refname:short)' --count=1"); 25 | 26 | var tag = proc.stdout.readLine(); 27 | tag = StringTools.trim(tag); 28 | tag = StringTools.replace(tag, "'", ""); 29 | trace("tag = |" + tag + "|"); 30 | Sys.command('git checkout -q $tag'); 31 | trace("checkout!"); 32 | Sys.setCwd(cwd); 33 | } 34 | 35 | function SetupGameSourceData() { 36 | var cwd = Sys.getCwd(); 37 | if (!FileSystem.exists("OneLifeGameSourceData")) { 38 | Sys.command('git clone https://github.com/PXshadow/OneLifeGameSourceData'); 39 | } 40 | Sys.setCwd("OneLifeGameSourceData"); 41 | var proc = new Process("git pull --force"); 42 | var line = proc.stdout.readLine(); 43 | trace('line |$line|'); 44 | Sys.setCwd(cwd); 45 | if (line != "Already up to date." || !FileSystem.exists("OneLifeData7/graphics")) { 46 | trace("copy dir!"); 47 | // copydir 48 | for (path in ["graphics", "settings", "languages", "groundTileCache"]) { 49 | FileTools.copyDir('OneLifeGameSourceData/$path', 'OneLifeData7/$path'); 50 | } 51 | FileTools.deleteDir("OneLifeGameSourceData"); 52 | } 53 | FileTools.deleteDir("OneLifeGameSourceData"); 54 | Sys.setCwd(cwd); 55 | } 56 | -------------------------------------------------------------------------------- /openlife/auto/Role.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.auto.actions.*; 4 | import openlife.engine.Engine; 5 | 6 | class Role { 7 | public var resourceNeeded:Int; 8 | public var name:String; 9 | public var actions:Array; 10 | public var inflowActions:Array; 11 | public var outflowActions:Array; 12 | public var actionsChecked:Array; 13 | 14 | // need to figure out how to assign actions to bot 15 | public function assignAction():Bool { 16 | // check isValidAction 17 | // check isValidTarget 18 | // set Bot's lastaction and currentaction 19 | return true; 20 | } 21 | 22 | // Think the action flow is mostly implemented 23 | // ####MAGIC WITH THE ACTION ARRAYS 24 | public function selectInflowAction():Action { 25 | for (i in inflowActions) { 26 | if (i.isValidAction() && !actionsChecked.contains(i.name)) { 27 | actionsChecked.push(i.name); 28 | return i; 29 | } 30 | } 31 | // default return to get rid of compile errors 32 | return null; 33 | } 34 | 35 | public function selectOutflowAction():Action { 36 | for (i in outflowActions) { 37 | if (i.isValidAction() && !actionsChecked.contains(i.name)) { 38 | actionsChecked.push(i.name); 39 | return i; 40 | } 41 | } 42 | return null; 43 | } 44 | 45 | public function selectAction():Action { 46 | actionsChecked = new Array(); 47 | if (resourceNeeded == 0 && this.inflowActions.length > 0) { 48 | return this.selectInflowAction(); 49 | } else if (this.outflowActions.length > 0) { 50 | return this.selectOutflowAction(); 51 | } else { 52 | // default cycle through actions array 53 | for (i in actions) { 54 | if (i.isValidAction() && !actionsChecked.contains(i.name)) { 55 | actionsChecked.push(i.name); 56 | return i; 57 | } 58 | } 59 | } 60 | return null; 61 | } 62 | 63 | public function nextAction():Action { 64 | return this.selectAction(); 65 | } 66 | 67 | // ###END MAGIC 68 | 69 | public function run(bot:BotType) { 70 | // Use select action 71 | // Check current action isValidAction 72 | // if action is valid then run action 73 | // otherwise next action 74 | bot.currentAction.step(bot); 75 | } 76 | 77 | public function assign(bot:BotType) { 78 | bot.role = this; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /openlife/server/ServerTag.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | // tags used by the server 4 | @:enum abstract ServerTag(Null) { 5 | public var KA = "KA"; 6 | public var USE = "USE"; 7 | public var BABY = "BABY"; 8 | public var SELF = "SELF"; 9 | public var UBABY = "UBABY"; 10 | public var REMV = "REMV"; 11 | public var SREMV = "SREMV"; 12 | public var DROP = "DROP"; 13 | public var SWAP = "SWAP"; 14 | public var KILL = "KILL"; 15 | public var JUMP = "JUMP"; 16 | public var EMOT = "EMOT"; 17 | public var DIE = "DIE"; 18 | public var GRAVE = "GRAVE"; 19 | public var OWNER = "OWNER"; 20 | public var FORCE = "FORCE"; 21 | public var PING = "PING"; 22 | // voice of god tags 23 | public var VOGS = "VOGS"; 24 | public var VOGN = "VOGN"; 25 | public var VOGP = "VOGP"; 26 | public var VOGM = "VOGM"; 27 | public var VOGI = "VOGI"; 28 | public var VOGT = "VOGT"; 29 | public var VOGX = "VOGX"; 30 | 31 | /** 32 | * photo to scary to use 33 | */ 34 | public var PHOTO = "PHOTO"; 35 | 36 | /** 37 | * say for messaging 38 | */ 39 | public var SAY = "SAY"; 40 | 41 | /** 42 | * login 43 | */ 44 | public var LOGIN = "LOGIN"; 45 | 46 | public var RLOGIN = "RLOGIN"; 47 | 48 | /** 49 | * move 50 | */ 51 | public var MOVE = "MOVE"; 52 | 53 | public var FLIP = "FLIP"; 54 | public var LEAD = "LEAD"; // Leader position request 55 | 56 | @:from private static function fromString(value:String):ServerTag { 57 | return switch (value) { 58 | case "KA": KA; 59 | case "USE": USE; 60 | case "BABY": BABY; 61 | case "SELF": SELF; 62 | case "UBABY": UBABY; 63 | case "REMV": REMV; 64 | case "SREMV": SREMV; 65 | case "DROP": DROP; 66 | case "SWAP": SWAP; 67 | case "KILL": KILL; 68 | case "JUMP": JUMP; 69 | case "EMOT": EMOT; 70 | case "DIE": DIE; 71 | case "GRAVE": GRAVE; 72 | case "OWNER": OWNER; 73 | case "FORCE": FORCE; 74 | case "PING": PING; 75 | case "VOGS": VOGS; 76 | case "VOGN": VOGN; 77 | case "VOGP": VOGP; 78 | case "VOGM": VOGM; 79 | case "VOGI": VOGI; 80 | case "VOGT": VOGT; 81 | case "VOGX": VOGX; 82 | case "PHOTO": PHOTO; 83 | case "SAY": SAY; 84 | case "LOGIN": LOGIN; 85 | case "RLOGIN": RLOGIN; 86 | case "MOVE": MOVE; 87 | case "FLIP": FLIP; 88 | case "LEAD": LEAD; 89 | default: null; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /openlife/client/Sound.hx: -------------------------------------------------------------------------------- 1 | import hxd.snd.ChannelGroup; 2 | import hxd.snd.effect.Spatialization; 3 | import hxd.snd.effect.Pitch; 4 | import hxd.snd.Effect; 5 | import haxe.io.UInt8Array; 6 | import hxd.fs.FileEntry; 7 | import hxd.snd.NativeChannel; 8 | import hxd.snd.Data; 9 | import hxd.snd.Manager; 10 | import hxd.snd.Channel; 11 | import hxd.snd.Data.SampleFormat; 12 | import openlife.resources.Resource; 13 | import haxe.io.Bytes; 14 | 15 | class Sound extends hxd.snd.Data { 16 | var rawData:Bytes; 17 | 18 | public function new(id:Int) { 19 | readMono16AIFFData(Resource.sound(id)); 20 | var res = new SoundRes(this); 21 | res.play(false, 1, Game.soundChannel); 22 | } 23 | 24 | private function readMono16AIFFData(data:Bytes) { 25 | if (data.length < 34) throw "Not long enough for header"; 26 | if (data.get(20) != 0 || data.get(21) != 1) throw "aiff not mono"; 27 | if (data.get(26) != 0 || data.get(27) != 16) throw "aiff not 16-bit"; 28 | var numSamples = data.get(22) << 24 | data.get(23) << 16 | data.get(24) << 8 | data.get(25); 29 | var sampleRate = data.get(30) << 8 | data.get(31); 30 | 31 | var sampleStartByte = 54; 32 | var numBytes = numSamples * 2; 33 | if (data.length < sampleStartByte + numBytes) throw "AIFF not long enough for data"; 34 | rawData = Bytes.alloc(numBytes); 35 | var b = sampleStartByte; 36 | for (i in 0...numSamples) { 37 | var value = (data.get(b) << 8) | data.get(b + 1); 38 | rawData.set(b - 54, (value) & 0xff); 39 | rawData.set(b + 1 - 54, (value >> 8) & 0xff); 40 | b += 2; 41 | } 42 | this.samplingRate = Std.int(sampleRate); 43 | this.channels = 1; 44 | this.sampleFormat = SampleFormat.I16; 45 | this.samples = Std.int(rawData.length / getBytesPerSample()); 46 | } 47 | 48 | override function decodeBuffer(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) { 49 | var bpp = getBytesPerSample(); 50 | out.blit(outPos, rawData, sampleStart * bpp, sampleCount * bpp); 51 | } 52 | } 53 | 54 | class SoundRes extends hxd.res.Sound { 55 | public function new(data:Data) { 56 | super(null); 57 | this.data = data; 58 | this.entry = new Entry(); 59 | } 60 | 61 | override function getData():Data { 62 | return data; 63 | } 64 | } 65 | 66 | class Entry extends FileEntry { 67 | public function new() {}; 68 | 69 | override function get_path():String { 70 | return ""; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /openlife/resources/Resource.hx: -------------------------------------------------------------------------------- 1 | package openlife.resources; 2 | 3 | import haxe.io.Path; 4 | import openlife.engine.Engine; 5 | import haxe.io.Bytes; 6 | 7 | @:expose 8 | class Resource { 9 | public static function objectData(id:Int):String { 10 | return getContent("objects", '$id'); 11 | } 12 | 13 | public static function spriteData(id:Int):String { 14 | return getContent("sprites", '$id'); 15 | } 16 | 17 | public static function spriteImage(id:Int):Bytes { 18 | return getImage("sprites", '$id'); 19 | } 20 | 21 | public static function sound(id:Int):Bytes { 22 | return bytes('sounds/$id.aiff'); 23 | } 24 | 25 | public static function music(id:Int):Bytes { 26 | var ids = Std.string(id); 27 | if (ids.length == 1) ids = '0$ids'; 28 | return bytes('music/music_$ids.ogg'); 29 | } 30 | 31 | public static function graphicImage(name:String):Bytes { 32 | return getImage("graphics", name); 33 | } 34 | 35 | public static function languageData(name:String):String { 36 | return getContent("languages", name); 37 | } 38 | 39 | public static function animation(id:Int, i:Int):String { 40 | return getContent("animations", '${id}_${i}'); 41 | } 42 | 43 | public static function ground(id:Int, i:Int, j:Int, a:String):Bytes { 44 | return getImage("groundTileCache", 'biome_${id}_x${i}_y$j$a'); 45 | } 46 | 47 | public static function groundOverlay(id:Int):Bytes { 48 | return getImage("graphics", 'ground_t$id'); 49 | } 50 | 51 | public static function dataVersionNumber():Int { 52 | return Std.parseInt(content("dataVersionNumber.txt")); 53 | } 54 | 55 | public static function getImage(path:String, name:String):Bytes { 56 | return bytes('$path/$name.tga'); 57 | } 58 | 59 | public static function getContent(path:String, name:String):String { 60 | path = Path.addTrailingSlash(path); 61 | return content('$path/$name.txt'); 62 | } 63 | 64 | // reciever that overrides old content and bytes system 65 | public static var recContent:String->String = null; 66 | public static var recBytes:String->Bytes = null; 67 | 68 | public static function content(path:String):String { 69 | if (recContent != null) return recContent(path); 70 | return sys.io.File.getContent('${Engine.dir}/$path'); 71 | } 72 | 73 | public static function bytes(path:String):Bytes { 74 | if (recBytes != null) return recBytes(path); 75 | return sys.io.File.getBytes('${Engine.dir}/$path'); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /openlife/settings/Settings.hx: -------------------------------------------------------------------------------- 1 | package openlife.settings; 2 | 3 | import haxe.DynamicAccess; 4 | import haxe.io.Path; 5 | import openlife.client.Client; 6 | import openlife.engine.Engine; 7 | import openlife.resources.Resource; 8 | #if (sys || nodejs) 9 | import sys.FileSystem; 10 | import sys.io.File; 11 | import sys.io.FileOutput; 12 | #end 13 | 14 | @:expose 15 | class Settings { 16 | @:isVar public var data(default, set):Data = {}; 17 | 18 | function set_data(value:Data):Data { 19 | var a = value.keys(); 20 | var b = data.keys(); 21 | if (a.length > b.length) { 22 | var name = a[a.length - 1] + ".ini"; 23 | var obj = value.get(name); 24 | // set settings 25 | var file = File.write(Engine.dir + "settings/" + name, false); 26 | file.writeString(obj); 27 | file.close(); 28 | } 29 | return data = value; 30 | } 31 | 32 | public function new() { 33 | var path:String = Engine.dir + "settings/"; 34 | if (!FileSystem.exists(path)) { 35 | FileSystem.createDirectory(Engine.dir + "settings"); 36 | } 37 | for (name in FileSystem.readDirectory(path)) { 38 | Reflect.setField(data, Path.withoutExtension(name), File.getContent(path + name)); 39 | } 40 | } 41 | 42 | var string:String; 43 | 44 | public function config():ConfigData { 45 | var config:ConfigData = { 46 | legacy: false, 47 | email: "test", 48 | key: "0000", 49 | ip: "localhost", 50 | port: 8005, 51 | seed: "", 52 | twin: "", 53 | tutorial: false 54 | }; 55 | // settings to use infomation 56 | if (valid(data.get("email"))) config.email = string; 57 | if (valid(data.get("accountKey"))) config.key = string; 58 | if (valid(data.get("useCustomServer")) && string == "1") { 59 | if (valid(data.get("customServerAddress"))) config.ip = string; 60 | if (valid(data.get("customServerPort"))) config.port = Std.parseInt(string); 61 | } 62 | // by pass settings and force email and key if secret account 63 | #if secret 64 | trace("set secret"); 65 | config.email = Secret.email; 66 | config.key = Secret.key; 67 | config.ip = Secret.ip; 68 | config.port = Secret.port; 69 | #end 70 | return config; 71 | } 72 | 73 | private inline function valid(obj:Dynamic):Bool { 74 | if (obj == null || obj == "") return false; 75 | string = cast obj; 76 | return true; 77 | } 78 | } 79 | 80 | @:expose 81 | typedef ConfigData = {?legacy:Bool, ?tag:String, ?email:String, ?key:String, ip:String, ?port:Int, ?seed:String, ?tutorial:Bool, ?twin:String} 82 | 83 | typedef Data = DynamicAccess 84 | -------------------------------------------------------------------------------- /openlife/resources/ObjectBake.hx: -------------------------------------------------------------------------------- 1 | package openlife.resources; 2 | 3 | import haxe.ds.Map; 4 | import openlife.engine.Engine; 5 | import haxe.ds.Vector; 6 | import openlife.data.object.ObjectData; 7 | import haxe.io.Path; 8 | 9 | /** 10 | * Bakes the numUses objects into files, rather than having to run through all the objects in the start of the session 11 | */ 12 | @:expose 13 | class ObjectBake { 14 | public static var nextObjectNumber:Int = 0; 15 | public static var baked:Bool = false; 16 | public static var dummies = new Map>(); 17 | public static var dummiesMap = new Map(); 18 | 19 | public static function finish() { 20 | #if (nodejs || sys) 21 | sys.io.File.saveContent(Engine.dir + "bake.res", Std.string(nextObjectNumber)); 22 | #end 23 | } 24 | 25 | public static function objectList():Vector { 26 | if (!sys.FileSystem.exists(Engine.dir + "objects/nextObjectNumber.txt")) { 27 | trace("object data failed to load"); 28 | trace("In order to fix run: haxe setup_data_client.hxml"); 29 | nextObjectNumber = 0; 30 | return null; 31 | } 32 | nextObjectNumber = Std.parseInt(sys.io.File.getContent(Engine.dir + "objects/nextObjectNumber.txt")); 33 | var list:Array = []; 34 | var num:Int = 0; 35 | for (path in sys.FileSystem.readDirectory(Engine.dir + "objects")) { 36 | num = Std.parseInt(Path.withoutExtension(path)); 37 | if (num > 0 && num < nextObjectNumber) { 38 | list.push(num); 39 | } 40 | } 41 | list.sort(function(a:Int, b:Int) { 42 | if (a > b) return 1; 43 | return -1; 44 | }); 45 | if (sys.FileSystem.exists(Engine.dir + "bake.res")) { 46 | baked = nextObjectNumber == Std.parseInt(sys.io.File.getContent(Engine.dir + "bake.res")); 47 | } 48 | return Vector.fromArrayCopy(list); 49 | } 50 | 51 | public static function objectData(vector:Vector):Array { 52 | var array:Array = []; 53 | var data:ObjectData; 54 | var i:Int = 0; 55 | for (id in vector) { 56 | data = new ObjectData(id); 57 | if (data.numUses > 1) { 58 | for (j in 1...data.numUses - 1) { 59 | data.id = 0; 60 | data.numUses = 0; 61 | data.dummy = true; 62 | data.dummyParent = data; 63 | array.push(data); 64 | } 65 | } 66 | } 67 | return array; 68 | } 69 | 70 | public static function dummy(obj:ObjectData) { 71 | var array = dummies.get(obj.dummyParent.id); 72 | if (array == null) array = []; 73 | array.push(obj.id); 74 | dummies.set(obj.dummyParent.id, array); 75 | dummiesMap.set(obj.id, obj.dummyParent.id); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /openlife/data/map/SceneCell.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.map; 2 | 3 | import haxe.ds.Vector; 4 | import openlife.data.animation.emote.Emotion; 5 | import openlife.data.animation.AnimationData; 6 | 7 | @:expose 8 | class SceneCell { 9 | /** 10 | * Biome id for ground 11 | */ 12 | public var biome:Int = 0; 13 | 14 | /** 15 | * Object id 16 | */ 17 | public var oID:Int = 0; 18 | 19 | /** 20 | * Held object id 21 | */ 22 | public var heldID:Int = 0; 23 | 24 | /** 25 | * Container format of vector of vector objects 26 | */ 27 | public var contained:Vector>; 28 | 29 | /** 30 | * Clothing object ids 31 | */ 32 | public var clothing:Array = []; 33 | 34 | /** 35 | * Flip horizontal 36 | */ 37 | public var flipH:Bool = false; 38 | 39 | /** 40 | * Age of cell 41 | */ 42 | public var age:Float = 0; 43 | 44 | /** 45 | * Held age of potential player 46 | */ 47 | public var heldAge:Float = 0; 48 | 49 | /** 50 | * Clothing of potential held player 51 | */ 52 | public var heldClothing:Array = []; 53 | 54 | /** 55 | * Emotion of potential held player 56 | */ 57 | public var heldEmotion:Emotion; 58 | 59 | /** 60 | * Animation playing 61 | */ 62 | public var anim:AnimationData = null; 63 | 64 | /** 65 | * Animation frozen in time 66 | */ 67 | public var frozenAnimTime:Float = 0; 68 | 69 | /** 70 | * Number of uses remaining 71 | */ 72 | public var numUsesRemaining:Int = 0; 73 | 74 | /** 75 | * Offset X 76 | */ 77 | public var xOffset:Int = 0; 78 | 79 | /** 80 | * Offset Y 81 | */ 82 | public var yOffset:Int = 0; 83 | 84 | /** 85 | * Destination cell offset x 86 | */ 87 | public var destCellXOffset:Int = 0; 88 | 89 | /** 90 | * Destination cell offset y 91 | */ 92 | public var destCellYOffset:Int = 0; 93 | 94 | /** 95 | * N/A 96 | */ 97 | public var moveFractionDone:Float = 0; 98 | 99 | /** 100 | * Movement of cell offset 101 | */ 102 | public var moveOffsetX:Float = 0; 103 | 104 | public var moveOffsetY:Float = 0; 105 | 106 | /** 107 | * Delay of movement 108 | */ 109 | public var moveDelayTime:Float = 0; 110 | 111 | /** 112 | * Start of movvement 113 | */ 114 | public var moveStartTime:Float = 0; 115 | 116 | /** 117 | * N/A 118 | */ 119 | public var frameCount:Int = 0; 120 | 121 | /** 122 | * N/A 123 | */ 124 | public var graveID:Int = 0; 125 | 126 | /** 127 | * Emotion of object cell 128 | */ 129 | public var currentEmot:Emotion; 130 | } 131 | -------------------------------------------------------------------------------- /openlife/server/ServerAi.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | import openlife.auto.Ai; 4 | import openlife.auto.AiBase; 5 | import openlife.auto.AiPx; 6 | import openlife.auto.AiPx; 7 | import openlife.settings.ServerSettings; 8 | 9 | class ServerAi { 10 | public static var AiIdIndex = 1; 11 | 12 | public var player:GlobalPlayerInstance; 13 | public var ai:AiBase; 14 | public var number:Int; 15 | public var connection:Connection; 16 | public var timeToRebirth:Float = 0; 17 | 18 | public function new(newPlayer:GlobalPlayerInstance) { 19 | ai = ServerSettings.NumberOfAiPx >= AiIdIndex ? new AiPx(newPlayer) : new Ai(newPlayer); 20 | this.number = AiIdIndex++; 21 | this.player = newPlayer; 22 | this.connection = player.connection; 23 | player.connection.serverAi = this; 24 | 25 | Connection.addAi(this); 26 | } 27 | 28 | public static function createNewServerAiWithNewPlayer():ServerAi { 29 | var email = 'AI${AiIdIndex}'; 30 | var newConnection = new Connection(null, Server.server); 31 | newConnection.playerAccount = PlayerAccount.GetOrCreatePlayerAccount(email, email); 32 | newConnection.playerAccount.isAi = true; 33 | var newPlayer = GlobalPlayerInstance.CreateNewAiPlayer(newConnection); 34 | 35 | var serverAi = new ServerAi(newPlayer); 36 | 37 | trace('new ai: ${serverAi.number} ${newConnection.playerAccount.email}'); 38 | 39 | serverAi.ai.newBorn(); 40 | return serverAi; 41 | } 42 | 43 | public function doRebirth(timePassedInSeconds:Float) { 44 | // TODO limit / increase Ais if serversettings change, or connected players change 45 | if (this.player.account.isAi == false) // it was a replacement for a player 46 | { 47 | trace('remove ai because it was to replace human: ${this.number}'); 48 | Connection.removeAi(this); 49 | return; 50 | } 51 | if (this.number > ServerSettings.NumberOfAis) { 52 | trace('remove ai because to many ai: ${this.number}'); 53 | Connection.removeAi(this); 54 | return; 55 | } 56 | 57 | if (timeToRebirth == 0) { 58 | var agefactor = Math.max(1, 60 - player.age); 59 | var waitingTime = agefactor * ServerSettings.TimeToAiRebirthPerYear; 60 | timeToRebirth = 10 + 2 * waitingTime * WorldMap.calculateRandomFloat(); 61 | } 62 | 63 | timeToRebirth -= timePassedInSeconds; 64 | 65 | if (timeToRebirth > 0) return; 66 | timeToRebirth = 0; 67 | // trace('doRebirth: '); 68 | 69 | this.player = GlobalPlayerInstance.CreateNewAiPlayer(connection); 70 | this.ai.myPlayer = player; // TODO same player for AI 71 | this.ai.newBorn(); 72 | } 73 | 74 | public function doTimeStuff(timePassedInSeconds:Float) { 75 | ai.doTimeStuff(timePassedInSeconds); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /openlife/server/ThreadServer.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | import openlife.macros.Macro; 4 | import haxe.io.Eof; 5 | import openlife.settings.ServerSettings; 6 | #if (target.threaded) 7 | import haxe.Timer; 8 | import haxe.Exception; 9 | import sys.net.Socket; 10 | import sys.thread.Thread; 11 | import sys.thread.Mutex; 12 | import sys.net.Host; 13 | 14 | class ThreadServer { 15 | public var socket:Socket; 16 | public var port:Int = 8005; 17 | public var maxCount:Int = -1; 18 | public var listenCount:Int = 10; 19 | 20 | public static inline var setTimeout:Int = 30; 21 | 22 | public var server:Server; 23 | 24 | public function new(server:Server, port:Int) { 25 | socket = new Socket(); 26 | this.port = port; 27 | this.server = server; 28 | } 29 | 30 | public function create() { 31 | socket.bind(new Host("0.0.0.0"), port); 32 | trace('listening on port: $port'); 33 | socket.listen(listenCount); 34 | while (true) { 35 | Thread.create(connection).sendMessage(socket.accept()); 36 | } 37 | } 38 | 39 | private function connection() { 40 | var socket:Socket = cast Thread.readMessage(true); 41 | trace('start connection ${Server.server.lastCommand}'); 42 | socket.setBlocking(ServerSettings.UseBlockingSockets); 43 | socket.setFastSend(true); 44 | var connection = new Connection(socket, server); 45 | var message:String = ""; 46 | var ka:Float = Timer.stamp(); 47 | 48 | while (connection.running) { 49 | try { 50 | Sys.sleep(ServerSettings.PlayerResponseSleepTime); 51 | 52 | message = socket.input.readUntil("#".code); 53 | 54 | // trace(message); 55 | ka = Timer.stamp(); 56 | } catch (e:Dynamic) { 57 | // error("---STACK---\n" + e.details()); 58 | 59 | if (e != haxe.io.Error.Blocked) { 60 | if ('$e' == 'Eof') { 61 | trace('Client closed connection / EOF'); 62 | } 63 | else trace('WARNING: EXEPTION: ' + e); 64 | 65 | Macro.exception(connection.close()); 66 | 67 | break; 68 | } 69 | else { 70 | if (Timer.stamp() - ka > 20) { 71 | Macro.exception(connection.close()); 72 | } 73 | } 74 | } 75 | 76 | if (message.length == 0) continue; 77 | 78 | if (ServerSettings.debug) { 79 | server.process(connection, message); 80 | message = ""; 81 | } 82 | else { 83 | try { 84 | server.process(connection, message); 85 | message = ""; 86 | } catch (e:Exception) { 87 | trace(e.details()); 88 | error("---STACK---\n" + e.details()); 89 | // connection.close(); 90 | continue; 91 | } 92 | } 93 | } 94 | 95 | trace("end connection"); 96 | } 97 | 98 | private function error(message:String) {} 99 | } 100 | #end 101 | -------------------------------------------------------------------------------- /app/App.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openlife.auto.Ai; 4 | import haxe.Exception; 5 | import openlife.client.Client; 6 | import haxe.Json; 7 | import sys.io.File; 8 | import openlife.data.object.ObjectData; 9 | import haxe.ds.Vector; 10 | import openlife.auto.Automation; 11 | import sys.FileSystem; 12 | import openlife.resources.ObjectBake; 13 | import openlife.settings.Settings; 14 | import haxe.ds.Map; 15 | import openlife.data.object.player.PlayerInstance; 16 | import openlife.engine.Program; 17 | import openlife.data.map.MapInstance; 18 | import openlife.engine.*; 19 | import openlife.data.object.player.PlayerMove; 20 | import openlife.data.map.MapChange; 21 | import openlife.auto.Overseer; 22 | import openlife.settings.OpenLifeData; 23 | import openlife.settings.OpenLifeData.OpenLifeData; 24 | import ClientAi.Bot; 25 | 26 | using StringTools; 27 | 28 | class App { 29 | public static var vector:Vector; 30 | 31 | var followingId:Int = -1; 32 | 33 | static var overseer = new Overseer(); 34 | 35 | public function new() { 36 | // openlife.auto.actions. 37 | Engine.dir = Utility.dir(); 38 | vector = Bake.run(); 39 | // start program 40 | var data = OpenLifeData.getData(); 41 | var config = new Settings().config(); 42 | if (data.syncSettings) { 43 | File.saveContent("data.json", Json.stringify(data)); 44 | } 45 | if (!FileSystem.exists("config.json")) { 46 | File.saveContent("config.json", Json.stringify(config)); 47 | } else { 48 | config = Json.parse(File.getContent("config.json")); 49 | } 50 | if (!data.relay && data.combo > 0) { 51 | // // multiple ais from combo 52 | // if (!FileSystem.exists("combo.txt")) throw "no combo list found"; 53 | // var list = File.getContent("combo.txt").split("\r\n"); 54 | // var ais:Array = []; 55 | // if (data.combo > list.length) data.combo = list.length; 56 | // for (i in 0...data.combo) { 57 | // var config = configClone(config); 58 | // var data = list[i].split(":"); 59 | // config.email = data[0]; 60 | // config.key = data[1]; 61 | // var client = new Client(); 62 | // client.config = config; 63 | // var ai = new Ai(client); 64 | // ai.connect(false, false); 65 | // bots.push(bot); 66 | // Sys.sleep(0.1); 67 | // } 68 | // trace("finish going through combo list length: " + data.combo); 69 | // while (true) { 70 | // overseer.run(bots); 71 | // Sys.sleep(1 / 120); 72 | // } 73 | } else { 74 | var client = new Client(); 75 | client.config = config; 76 | var bot = new Bot(client); 77 | bot.relayPort = 8000; 78 | bot.connect(false, data.relay); 79 | while (true) { 80 | bot.update(); 81 | Sys.sleep(1 / 20); 82 | } 83 | } 84 | } 85 | 86 | private function configClone(cred:ConfigData):ConfigData { 87 | return { 88 | email: cred.email, 89 | key: cred.key, 90 | ip: cred.ip, 91 | port: cred.port, 92 | tutorial: cred.tutorial, 93 | seed: cred.seed, 94 | twin: cred.twin, 95 | legacy: cred.legacy 96 | }; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /openlife/data/FractalNoise.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | #if cpp 4 | import cpp.UInt64; 5 | import cpp.UInt32; 6 | #elseif hl 7 | import UInt as UInt64; 8 | import UInt as UInt32; 9 | #end 10 | 11 | /* 12 | class FractalNoise 13 | { 14 | 15 | //private static inline var XX_PRIME32_1:UInt64 = cast 2654435761; 16 | //private static inline var XX_PRIME32_2:UInt64 = cast 2246822519; 17 | //private static inline var XX_PRIME32_3:UInt64 = cast 3266489917; 18 | //private static inline var XX_PRIME32_4:UInt64 = cast 668265263; 19 | //private static inline var XX_PRIME32_5:UInt64 = cast 374761393; 20 | 21 | 22 | private static inline var XX_PRIME32_1:UInt64 = cast 26544357; 23 | private static inline var XX_PRIME32_2:UInt64 = cast 22468225; 24 | private static inline var XX_PRIME32_3:UInt64 = cast 32664899; 25 | private static inline var XX_PRIME32_4:UInt64 = cast 6682652; 26 | private static inline var XX_PRIME32_5:UInt64 = cast 3747613; 27 | private static var xxSeed:UInt32 = 0; 28 | public static function setXYRandomSeed(inSeed:UInt32) 29 | { 30 | xxSeed = inSeed; 31 | } 32 | public static function xxTweakedHash2D(inX:UInt32,inY:UInt32):UInt32 33 | { 34 | var h32:UInt32 = cast(xxSeed + inX + XX_PRIME32_5); 35 | var add:UInt32 = cast(inY * XX_PRIME32_3); 36 | h32 += add; 37 | h32 *= cast XX_PRIME32_2; 38 | h32 ^= cast h32 >> 13; 39 | h32 *= cast XX_PRIME32_3; 40 | h32 ^= cast h32 >> 16; 41 | return h32; 42 | } 43 | //private static var oneOverIntMax = 1.0 / 4294967295; 44 | private static var oneOverIntMax = 1.0 / 429496; 45 | public static function getXYRandom(inX:Int,inY:Int):Float 46 | { 47 | return xxTweakedHash2D(inX,inY) * oneOverIntMax; 48 | } 49 | public static function getXYRandomBN(inX:Float,inY:Float):Float 50 | { 51 | var floorX:Int = Math.floor(inX); 52 | var ceilX = floorX + 1; 53 | var floorY = Math.floor(inY); 54 | var ceilY = floorY + 1; 55 | 56 | var cornerA1:Float = xxTweakedHash2D(floorX,floorY); 57 | var cornerA2:Float = xxTweakedHash2D(ceilX,floorY); 58 | 59 | var cornerB1 = xxTweakedHash2D(floorX,ceilY); 60 | var cornerB2 = xxTweakedHash2D(ceilX,ceilY); 61 | 62 | var xOffset:Float = inX - floorX; 63 | var yOffset:Float = inY - floorY; 64 | 65 | var topBlend:Float = cornerA2 * xOffset + (1-xOffset) * cornerA1; 66 | var bottomBlend:Float = cornerB2 * xOffset + (1-xOffset) * cornerB1; 67 | 68 | return bottomBlend * yOffset + (1-yOffset) * topBlend; 69 | } 70 | public static function getXYFractal(inX:Int,inY:Int,inRoughness:Float,inScale:Float):Float 71 | { 72 | var b:Float = inRoughness; 73 | var a:Float = 1 - b; 74 | 75 | var sum:Float = 0 + 76 | a * getXYRandomBN(inX/(32 * inScale),inY/(32 * inScale)) + 77 | b * ( 78 | a * getXYRandomBN(inX/(16 * inScale),inY/(16 * inScale)) + 79 | b * ( 80 | a * getXYRandomBN(inX/(8 * inScale),inY/(8 * inScale)) + 81 | b * ( 82 | a * getXYRandomBN(inX/(4 * inScale),inY/(4 * inScale)) + 83 | b * ( 84 | a * getXYRandomBN(inX/(2 * inScale),inY/(2 * inScale)) + 85 | b * ( 86 | a * getXYRandomBN(inX/inScale,inY/inScale) 87 | ))))); 88 | return sum * oneOverIntMax; 89 | } 90 | } 91 | */ 92 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 |

2 | 3 | [![badge](https://img.shields.io/discord/595575978290446361?style=plastic)](https://discordapp.com/invite/gwghtky) 4 | ![Stars](https://img.shields.io/github/stars/pxshadow/openlife?style=social) 5 | 6 | 7 | [![badge](badge.svg)](https://haxe.org) 8 | [![forthebadge](https://forthebadge.com/images/badges/for-sharks.svg)](https://forthebadge.com) 9 | 10 | Summary 11 | ====== 12 | 13 | *Open Life is a [Haxe](https://haxe.org) based OneLife engine. (the server is in beta the client is in early alpha state)* 14 | * **Easy creation:** Build a client/bot (server in the future) easily for any OneLife server supported protocol (OneLife and TwoLife etc) 15 | 16 | * **Open Api:** Quickly search through the [api](https://pxshadow.github.io/OpenLife-Docs/api/index.html) and find variables and methods you need to create a new project. 17 | 18 | * **Scripting:** Allows programmers to easily build programs on top of the engine architecture, using an event model. Can also be interpreted at runtime using the [hscript library](https://github.com/HaxeFoundation/hscript). 19 | 20 | * **Easy to use build tools:** Unlike c++ the build tools are unified. A simple npm install will download the needed haxe language version, then download vscode download the haxe extension and Hashlink debugger and you can easily compile and debug the project :) (Currently will also need to manually setup Hashlink until it's integrated into lix) 21 | 22 | 23 | * **Cross compiled languages:** Compiles to [Java](https://haxe.org/documentation/platforms/java.html), [C#](https://haxe.org/documentation/platforms/csharp.html), [Python](https://haxe.org/documentation/platforms/python.html), [Lua](https://haxe.org/manual/target-lua-getting-started.html), [Php](https://haxe.org/documentation/platforms/php.html), [Javascript/NodeJS](https://haxe.org/documentation/platforms/javascript.html) 24 | 25 | * **Thoughtful, concise and efficient code:** Codebase is designed around ease of use and collaboration and continues to become as minimalis as possible while also keeping the level of abstraction low. 26 | 27 | UPDATE: (Arcurus) 28 | Currenty the project has three parts. A server, a client and a relay (headless client) 29 | * The server.hxml project is a reimplementation of vanilla server with lot of changes compared to vanilla. It is fully playable and in BETA. 30 | The server / Open Life Reborn can run around 50 AIs which are able to run a village inclusive composting and some basic smithing 31 | How to set the server up plus a complete change log you can find here: 32 | https://github.com/PXshadow/OpenLife/blob/master/TODO.MD/ 33 | * The client.hxml is currently very basic and can load all the needed stuff to do animations and connect to the server but does not yet send player commands to the server 34 | * The relay (app.hxml?) can listen to any vanilla Client commands and send them to the server and the other way round. Here you can add your client based AI / It currently needs some little work to run again since focus was on implementing a server AI 35 | 36 | The server AI uses an Interface to abstract if it runs on client or server but currently would need some work to be adapted to run client side. 37 | 38 | 39 | -------------------------------------------------------------------------------- /WebServer/OpenLifeReborn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to Open Life Reborn 5 | 18 | 19 | 20 |

Welcome to Open Life Reborn

21 |

Open Life Reborn is a free roleplay multiplayer civilisation building and survival game on the base of One Hour One Life from Jason Rohrer. You can play Open Life Reborn for free with any One Hour One Life Client by entering OpenLifeReborn.com as a custom server with the default port 8005.

22 | 23 |

Feel welcome to join the Discord Community.

24 | 25 |

In case you have no client you can use This Free Client at your own risk!

26 | 27 |

And most importantly have fun!

28 |

29 | Rules 30 | Features 31 | Statistics 32 |

33 | 34 |
35 | Open Life Reborn by Dayemeg - Free OHOL 36 |
Much thx to Dayemeg for the banner! 37 |
38 |

By playing you accept these rules:

39 |

1. Have fun and make sure that also fellow players have fun!

40 |

2. Dive into the world and try to roleplay in a medieval fantasy setting

41 |

3. If you happen to be born as noble be a noble!

42 |

4. If you want to play an antagonist first ask the moderators in the Discord Community

43 |

5. What happens in game happens in game but remember rule 1

44 |

Features:

45 |
    46 |
  • Round Map with rivers, bridges mountains and oceans
  • 47 |
  • No family iron, family wells, biome locking but with biome boni
  • 48 |
  • Prestige score system based on eating Yummy food and taking care of your family
  • 49 |
  • New combat system with different weapon damage / hit points
  • 50 |
  • Different seasons that affect temperature and plant growth
  • 51 |
  • Cold or heat can and will kill you! Be prepared!
  • 52 |
  • New in game currency
  • 53 |
  • AI NPCs that can be hired and keep your family alive
  • 54 |
  • AI takes over until you reconnect
  • 55 |
56 |

Open Life Reborn is a community driven non commercial project. Use all links and linked software at your own risk! You can contact me at martin_auer(at).gmx.net or in above linked Discord

57 |

Statistics

58 | 59 | -------------------------------------------------------------------------------- /FAQ.MD: -------------------------------------------------------------------------------- 1 | Frequently asked questions 2 | ============ 3 | 4 | * Question: How do changes made to data objects get sent to the client/server 5 | 6 | Answer: [protocol.txt](protocol.txt) documents how the messages are exchanged both from the client perspective and server side. 7 | 8 | * Question: What is the structure for the project? 9 | 10 | Answer: The project is broken up into 4 main parts client/data/server/engine 11 | 12 | client + data + engine are used by the client and server + data are used by the server. 13 | 14 | ## Client 15 | has the [Client.hx](openlife/client/Client.hx) class that holds the Socket and how it sends and responds to messages from the server. [ClientTag.hx](openlife/client/ClientTag.hx) holds an enum that connect to the tags the client receives from the server. 16 | 17 | ## Data 18 | is a large package that stores all of the data classes for the game, including object/map/player data. 19 | 20 | ## Engine 21 | is a package used by the client only to allow easy extension/implementation of high level events and program functions. It turns the low level dealing with the data by the client into higher level processes for example 22 | from: 23 | ``` 24 | tag = PLAYER_SAYS, 35/0 HELLO WORLD 25 | ``` 26 | to: 27 | ```haxe 28 | playerSays(id:Int,curse:Bool,text:String) 29 | ``` 30 | for sending it also uses the same principle and uses the class [Program.hx](openlife/engine/Program.hx) 31 | from: 32 | ```haxe 33 | send("SAYS 0 0 HELLO WORLD"); 34 | ``` 35 | to: 36 | ```haxe 37 | program.say("HELLO WORLD"); 38 | ``` 39 | 40 | ## Server 41 | 42 | is a package used only by the server to allow straight forward generation of the world and connection of hundreds of clients at once. It currently is only on it's fire iteration so things may vary alot and be refined so the api's are more unified from the server and the client. Right now there is a [ThreadServer.hx](openlife/server/ThreadServer.hx) that is the low level system that generates one thread per Socket connection and handles the updating. [ServerTag.hx](openlife/client/ServerTag.hx) holds an enum that connect to the tags the server receives from the client. [Map.hx](openlife/server/Map.hx) is responsible for the map data and generation (in the future it should probably be extended by data package's [MapData](openlife/data/map/MapData.hx)). Server holds the global functions that are independent of a single connection. [Connection.hx](openlife/server/Connection.hx) class has the functions that are run in reference to a single connection to the server. 43 | 44 | * Question: When did this project start? 45 | 46 | Answer: June 5th 2019 is the first github commit. 47 | 48 | * Question: Why is this being worked on? 49 | 50 | Answer: the reasons why have shifted throughout time but the core idea has remained, because it's worth perusing. New breakthroughs have lead to most of the reasons shifting and have also scaled the project immensely from a simple buggy visual Client, to a competitive alternative to OneLife's vanilla implementation designed as the best engine to power clients/bots/mods/servers in OneLife across programming languages. 51 | 52 | * Question: what can you use this for? 53 | 54 | Answer: the library core for a new modded client/client scripts/bots/modded server etc following the OneLife protocol. In any of these languages Haxe, Javascript, Java, Python, Lua, C++, C, C# etc. -------------------------------------------------------------------------------- /scripts/ProtocolTagCheck.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import sys.io.File; 4 | import sys.FileSystem; 5 | 6 | using StringTools; 7 | 8 | class ProtocolTagCheck { 9 | public static function main() { 10 | new ProtocolTagCheck(); 11 | } 12 | 13 | var startString = "Possible types include:"; 14 | var endString = "Client can expect to receive these at any time, and in any order."; 15 | var clientTagPath = "./openlife/client/ClientTag.hx"; 16 | // more tags 17 | var enumTags:Array = []; 18 | var names:Array = []; 19 | var switchTags:Array = []; 20 | var string:String; 21 | var headerContent:String; 22 | var gameContent:String; 23 | var errors:Int = 0; 24 | 25 | public function new() { 26 | string = haxe.Http.requestUrl("https://raw.githubusercontent.com/jasonrohrer/OneLife/master/server/protocol.txt"); 27 | if (string != File.getContent("./protocol.txt")) { 28 | trace("protocol is not the same, update"); 29 | File.saveContent("./protocol.txt", string); 30 | } 31 | headerContent = File.getContent("./openlife/engine/engineHeader.hx"); 32 | gameContent = File.getContent("./openlife/engine/Engine.hx"); 33 | run(); 34 | } 35 | 36 | private function run() { 37 | trace("start protocol tag check"); 38 | var index = string.indexOf(startString) + startString.length; 39 | if (index <= startString.length) { 40 | trace('failed to find start string, index: $index'); 41 | return; 42 | } 43 | string = string.substring(index, string.indexOf(endString, index)); 44 | var tags:Array = []; 45 | // data = data.replace("\n",""); 46 | for (obj in string.split("\n")) { 47 | index = obj.indexOf("("); 48 | if (index == -1) continue; 49 | names.push(StringTools.rtrim(obj.substring(0, index))); 50 | tags.push(obj.substring(index + 1, obj.indexOf(")", index))); 51 | } 52 | // check clientTag 53 | string = File.getContent(clientTagPath); 54 | index = string.indexOf("{"); 55 | var sub = string.substring(index, index = string.indexOf("@:from", index)); 56 | string = string.substring(index = string.indexOf("case", index), string.indexOf("}", index)); 57 | 58 | generate(sub.split("\n"), enumTags); 59 | generate(string.split("\n"), switchTags, true); 60 | 61 | var error:Array = []; 62 | for (i in 0...tags.length) { 63 | error = []; 64 | if (enumTags.indexOf(tags[i]) == -1) error.push("enum"); 65 | if (switchTags.indexOf(tags[i]) == -1) error.push("switch"); 66 | if (error.length > 0) { 67 | if (error.length > 1) { 68 | trace(names[i] + " " + tags[i] + " | " + error[0] + " and " + error[1] + " not found"); 69 | errors++; 70 | } else { 71 | trace(names[i] + " " + tags[i] + " | " + error[0] + " not found"); 72 | errors++; 73 | } 74 | } 75 | // header content 76 | if (headerContent.indexOf("//" + names[i]) == -1) { 77 | trace("EngineHeader.hx could not find: " + names[i]); 78 | errors++; 79 | } 80 | if (gameContent.indexOf("case " + names[i]) == -1) { 81 | trace("Engine.hx could not find: " + names[i]); 82 | errors++; 83 | } 84 | } 85 | trace('finished with $error errors'); 86 | } 87 | 88 | private function generate(input:Array, output:Array, name:Bool = false) { 89 | var index:Int = 0; 90 | for (obj in input) { 91 | index = obj.indexOf('"') + 1; 92 | if (index == 0) continue; 93 | index = output.push(obj.substring(index, obj.indexOf('"', index))); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /openlife/auto/Automation.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.data.object.ObjectData; 4 | import openlife.resources.ObjectBake; 5 | import openlife.data.object.player.PlayerInstance; 6 | import openlife.data.map.MapData; 7 | import openlife.data.Pos; 8 | import haxe.ds.Vector; 9 | import openlife.engine.Program; 10 | 11 | /** 12 | * nick name auto, will be a powerful class that uses program, and transition data to do automatic tasks. 13 | */ 14 | @:expose 15 | class Automation { 16 | public var program:Program; 17 | public var interp:Interpreter; 18 | 19 | public function new(program:Program) { 20 | this.program = program; 21 | interp = new Interpreter(); 22 | } 23 | 24 | public function goto(id:Array, buffer:Pos->Void):Bool { 25 | var pos = select(id); 26 | if (pos == null) return false; 27 | if (!program.goto(pos.x, pos.y)) return false; 28 | buffer(pos); 29 | return true; 30 | } 31 | 32 | public function foreach(func:(x:Int, y:Int, array:Array) -> Bool, repeat:Bool = true) { 33 | @:privateAccess for (y in program.player.y - MapData.RAD...program.player.y + MapData.RAD) { 34 | @:privateAccess for (x in program.player.x - MapData.RAD...program.player.x + MapData.RAD) { 35 | var array = @:privateAccess program.map.object.get(x, y); 36 | if (array != null) { 37 | var bool = func(x, y, array); 38 | if (!repeat && bool) return; 39 | } 40 | } 41 | } 42 | } 43 | 44 | public function select(id:Array):Pos { 45 | var array = ObjectBake.dummies.get(id[0]); 46 | if (array != null) id = id.concat(array); 47 | var dis:Float = 2000; 48 | var pos:Pos = null; 49 | foreach(function(x:Int, y:Int, array:Array) { 50 | for (i in 0...array.length) { 51 | if (id.indexOf(array[i]) > -1) { 52 | @:privateAccess var tdis = Math.abs(x - program.player.x) + Math.abs(y - program.player.y); 53 | if (dis > tdis) { 54 | dis = tdis; 55 | pos = new Pos(x, y); 56 | if (i > 0) pos.y += new ObjectData(array[0]).noBackAcess ? -1 : 0; 57 | } 58 | } 59 | } 60 | return true; 61 | }); 62 | return pos; 63 | } 64 | 65 | public function get(id:Array):Array> { 66 | var obj:Array = []; 67 | var pos:Array = []; 68 | foreach(function(x:Int, y:Int, array:Array) { 69 | for (i in 0...array.length) { 70 | if (id.indexOf(array[i]) > -1) { 71 | var p = new Pos(x, y); 72 | pos.push(p); 73 | if (i > 0) { 74 | var sy = new ObjectData(array[0]).noBackAcess ? -1 : 0; 75 | var p = p.clone(); 76 | p.y += sy; 77 | obj.push(p); 78 | } else { 79 | obj.push(p); 80 | } 81 | } 82 | } 83 | return true; 84 | }); 85 | return [pos, obj]; 86 | } 87 | 88 | public function food() { 89 | var pos:Pos = null; 90 | foreach(function(x:Int, y:Int, array:Array) { 91 | for (i in 0...array.length) { 92 | var obj = new ObjectData(array[i]); 93 | if (obj.foodValue > 0) { 94 | pos = new Pos(x, y); 95 | var sy:Int = 0; 96 | if (i > 0) sy = new ObjectData(array[0]).noBackAcess ? -1 : 0; 97 | // no repeat so this is last function call 98 | if (!program.goto(pos.x, pos.y + sy)) return true; 99 | program.use(pos.x, pos.y); 100 | program.self(); 101 | return true; 102 | } 103 | } 104 | return false; 105 | }, false); 106 | } 107 | } 108 | 109 | typedef Auto = Automation; 110 | -------------------------------------------------------------------------------- /openlife/server/SerializeHelper.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | import openlife.data.object.player.PlayerInstance; 4 | import sys.io.File; 5 | import openlife.data.object.ObjectData; 6 | import openlife.settings.ServerSettings; 7 | 8 | class SerializeHelper { 9 | public static function createReadWriteFile() { 10 | var rtti = haxe.rtti.Rtti.getRtti(GlobalPlayerInstance); 11 | var dir = './${ServerSettings.SaveDirectory}/'; 12 | var loadText = ''; 13 | var endtext = ''; 14 | var writer = File.write(dir + "writerGlobalPlayerInstance.txt", false); 15 | var writer2 = File.write(dir + "readerGlobalPlayerInstance.txt", false); 16 | 17 | var count = 0; 18 | 19 | for (field in rtti.fields) { 20 | count++; 21 | 22 | var typename = '${field.type}'; 23 | 24 | switch (typename) { 25 | case "CAbstract(Int,[])": 26 | writer.writeString('\t\twriter.writeInt32(obj.${field.name});\n'); 27 | writer2.writeString('\t\tobj.${field.name} = reader.readInt32();\n'); 28 | // trace('FOUND: ${field.type.getName} ${field.type}'); // Int 29 | 30 | case "CAbstract(Float,[])": 31 | writer.writeString('\t\twriter.writeFloat(obj.${field.name});\n'); 32 | writer2.writeString('\t\tobj.${field.name} = reader.readFloat();\n'); 33 | 34 | case "CAbstract(Bool,[])": 35 | writer.writeString('\t\twriter.writeInt8(obj.${field.name} ? 1 : 0);\n'); 36 | writer2.writeString('\t\tobj.${field.name} = reader.readInt8() != 0 ? true : false;\n'); 37 | 38 | case "CClass(String,[])": 39 | writer.writeString('\t\twriter.writeInt16(obj.${field.name}.length);\n'); 40 | writer.writeString('\t\twriter.writeString(obj.${field.name});\n'); 41 | 42 | writer2.writeString('\t\tvar len = reader.readInt16();\n'); 43 | writer2.writeString('\t\tobj.${field.name} = reader.readString(len);\n'); 44 | 45 | case "CClass(Array,[CAbstract(Int,[])])": 46 | writer.writeString('\t\twriter.writeInt16(obj.${field.name}.length);\n'); 47 | writer.writeString('\t\tfor(i in obj.${field.name}) writer.writeInt32(i);\n'); 48 | 49 | writer2.writeString('\t\tobj.${field.name} = new Array();\n'); 50 | writer2.writeString('\t\tvar len = reader.readInt16();\n'); 51 | writer2.writeString('\t\tfor(i in 0...len){obj.${field.name}[i] = reader.readInt32();}\n'); 52 | case "CClass(openlife.server.GlobalPlayerInstance,[])": 53 | writer.writeString('\t\twriter.writeInt32(GetPlayerIdForWrite(obj.${field.name})); //$count\n'); 54 | writer2.writeString('\t\tplayersToLoad[obj.p_id]["${field.name}"] = reader.readInt32(); //$count\n'); 55 | loadText += '\t\tobj.${field.name} = GetPlayerFromId(playersToLoad[obj.p_id]["${field.name}"]); //$count\n'; 56 | case "CClass(openlife.data.object.ObjectHelper,[])": 57 | writer.writeString('\t\tObjectHelper.WriteToFile(obj.${field.name}, writer); //$count\n'); 58 | writer2.writeString('\t\tobj.${field.name} = ObjectHelper.ReadFromFile(reader); //$count\n'); 59 | default: 60 | if (StringTools.contains(typename, 'CFunction') == false) { 61 | trace('FIELD: ${field.name} $count ${field.type}'); 62 | writer.writeString('\t\t//${field.name} $count ${field.type}\n'); 63 | writer2.writeString('\t\t//${field.name} $count ${field.type}\n'); 64 | endtext += '\t\t//${field.name} $count ${field.type}\n'; 65 | } 66 | } 67 | } 68 | 69 | writer.writeString('\n' + endtext); 70 | writer2.writeString('\n' + loadText); 71 | 72 | writer.close(); 73 | writer2.close(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /openlife/client/World.hx: -------------------------------------------------------------------------------- 1 | import openlife.data.map.MapChange; 2 | import openlife.data.map.MapInstance; 3 | import openlife.data.object.player.PlayerInstance; 4 | import openlife.data.object.player.PlayerMove; 5 | import openlife.engine.EngineHeader; 6 | 7 | class World implements EngineHeader { 8 | public function new() {} 9 | 10 | public function playerUpdate(instances:Array) {} 11 | 12 | public function playerMoveStart(move:PlayerMove) {} 13 | 14 | public function playerOutOfRange(list:Array) {} 15 | 16 | public function playerName(id:Int, firstName:String, lastName:String) {} 17 | 18 | public function apocalypse() {} 19 | 20 | public function apocalypseDone() {} 21 | 22 | public function posse(killer:Int, target:Int) {} 23 | 24 | public function following(follower:Int, leader:Int, color:Int) {} 25 | 26 | public function exiled(target:Int, id:Int) {} 27 | 28 | public function cursed(id:Int, level:Int, word:String) {} 29 | 30 | public function curseToken(count:Int) {} 31 | 32 | public function curseScore(excess:Int) {} 33 | 34 | public function badBiomes(id:Int, name:String) {} 35 | 36 | public function vogUpdate() {} 37 | 38 | public function photo(x:Int, y:Int, signature:String) {} 39 | 40 | public function shutdown() {} 41 | 42 | public function global(text:String) {} 43 | 44 | public function war(a:Int, b:Int, status:String) {} 45 | 46 | public function learnedTools(list:Array) {} 47 | 48 | public function toolExperts(list:Array) {} 49 | 50 | public function toolSlots(total:Int) {} 51 | 52 | public function babyWiggle(list:Array) {} 53 | 54 | public function saysLocation(x:Int, y:Int, text:String) {} 55 | 56 | public function dying(id:Int, sick:Bool) {} 57 | 58 | public function says(id:Int, text:String, curse:Bool) {} 59 | 60 | public function emot(id:Int, index:Int, sec:Int) {} 61 | 62 | public function mapChunk(instance:MapInstance) { 63 | var progress = 0; 64 | for (y in 0...instance.height) { 65 | for (x in 0...instance.width) { 66 | if (progress % 10 == 0) trace(progress / (instance.width * instance.height)); 67 | progress++; 68 | final groundId = Game.engine.map.biome.get(instance.x + x, instance.y + y); 69 | Render.addGround(x, y, groundId); 70 | Render.addObject(x, y, Game.engine.map.object.get(instance.x + x, instance.y + y)); 71 | } 72 | } 73 | } 74 | 75 | public function mapChange(change:MapChange) {} 76 | 77 | public function foodChange(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int) {} 78 | 79 | public function heatChange(heat:Float, foodTime:Float, indoorBonus:Float) {} 80 | 81 | public function frame() {} 82 | 83 | public function lineage(list:Array, eve:Int) {} 84 | 85 | public function healed(id:Int) {} 86 | 87 | public function monument(x:Int, y:Int, id:Int) {} 88 | 89 | public function grave(x:Int, y:Int, id:Int) {} 90 | 91 | public function graveOld(x:Int, y:Int, pid:Int, poid:Int, age:Float, name:String, lineage:Array) {} 92 | 93 | public function graveMove(xs:Int, ys:Int, xd:Int, yd:Int, swapDest:Bool) {} 94 | 95 | public function ownerList(x:Int, y:Int, list:Array) {} 96 | 97 | public function valley(spacing:Int, offset:Int) {} 98 | 99 | public function flight(id:Int, x:Int, y:Int) {} 100 | 101 | public function homeland(x:Int, y:Int, name:String) {} 102 | 103 | public function craving(id:Int, bonus:Int) {} 104 | 105 | public function flip(x:Int, y:Int) {} 106 | } 107 | -------------------------------------------------------------------------------- /openlife/data/animation/AnimationParameter.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation; 2 | 3 | @:expose 4 | class AnimationParameter { 5 | // https://github.com/twohoursonelife/OneLifeDocs/blob/master/OHOL%20editor%20guide.txt 6 | 7 | /** 8 | * Offset of sprite 9 | */ 10 | public var offsetX:Float = 0; 11 | 12 | public var offsetY:Float = 0; 13 | 14 | /** 15 | * the first pause, before the first animation duration, 16 | * for controling the phase of the duration-pause cycle 17 | */ 18 | public var startPauseSec:Float = 0; 19 | 20 | /** 21 | * param array start here, amount of seconds to complete cycle 22 | */ 23 | public var xOscPerSec:Float = 0; 24 | 25 | /** 26 | * in pixels distance from middle 27 | */ 28 | public var xAmp:Float = 0; 29 | 30 | /** 31 | * between 0 and 1 of where to start the animation 0 = middle-right, 0.25 = right-left, 0.50, 0.50 middle-left, 0.75 left-right 32 | */ 33 | public var xPhase:Float = 0; 34 | 35 | public var yOscPerSec:Float = 0; 36 | public var yAmp:Float = 0; 37 | public var yPhase:Float = 0; 38 | 39 | public var rotationCenterOffsetX:Float = 0; 40 | public var rotationCenterOffsetY:Float = 0; 41 | // can be positive (CW) or negative (CCW) 42 | public var rotPerSec:Float = 0; 43 | public var rotPhase:Float = 0; 44 | 45 | public var rockOscPerSec:Float = 0; 46 | 47 | /** 48 | * between 0 and 1, where 1 is full rotation before coming back 49 | */ 50 | public var rockAmp:Float = 0; 51 | 52 | public var rockPhase:Float = 0; 53 | 54 | /** 55 | * for animations that run for a while and pause between runs 56 | * between 0 and 1, where 1 is full rotation before coming back 57 | */ 58 | public var durationSec:Float = 0; 59 | 60 | public var pauseSec:Float = 0; 61 | 62 | public var fadeOscPerSec:Float = 0; 63 | 64 | /** 65 | * 0 is sine wave, 1 is square wave, in between is a mix 66 | */ 67 | public var fadeHardness:Float = 0; 68 | 69 | /** 70 | * what fade oscillates between max 71 | */ 72 | public var fadeMin:Float = 0; 73 | 74 | /** 75 | * what fade oscillates between min 76 | */ 77 | public var fadeMax:Float = 0; 78 | 79 | /** 80 | * between 0 and 1 81 | */ 82 | public var fadePhase:Float = 0; 83 | 84 | public function new() {} 85 | 86 | /** 87 | * Process parameter 88 | * @param array properties 89 | */ 90 | public function process(array:Array) { 91 | var i:Int = 0; 92 | xOscPerSec = Std.parseFloat(array[i++]); 93 | xAmp = Std.parseFloat(array[i++]); 94 | xPhase = Std.parseFloat(array[i++]); 95 | 96 | yOscPerSec = Std.parseFloat(array[i++]); 97 | yAmp = Std.parseFloat(array[i++]); 98 | yPhase = Std.parseFloat(array[i++]); 99 | 100 | var s:String = array[i++]; 101 | var cut = s.indexOf(","); 102 | rotationCenterOffsetX = Std.parseFloat(s.substring(1, cut)); 103 | rotationCenterOffsetY = Std.parseFloat(s.substring(cut + 1, s.length - 1)); 104 | 105 | rotPerSec = Std.parseFloat(array[i++]); 106 | rotPhase = Std.parseFloat(array[i++]); 107 | 108 | rockOscPerSec = Std.parseFloat(array[i++]); 109 | rockAmp = Std.parseFloat(array[i++]); 110 | rockPhase = Std.parseFloat(array[i++]); 111 | 112 | durationSec = Std.parseFloat(array[i++]); 113 | pauseSec = Std.parseFloat(array[i++]); 114 | 115 | fadeOscPerSec = Std.parseFloat(array[i++]); 116 | fadeHardness = Std.parseFloat(array[i++]); 117 | fadeMin = Std.parseFloat(array[i++]); 118 | fadeMax = Std.parseFloat(array[i++]); 119 | fadePhase = Std.parseFloat(array[i++]); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /openlife/engine/EngineEvent.hx: -------------------------------------------------------------------------------- 1 | package openlife.engine; 2 | 3 | import openlife.data.map.MapInstance; 4 | import openlife.data.object.player.PlayerInstance; 5 | import openlife.data.object.player.PlayerMove; 6 | import openlife.data.map.MapChange; 7 | 8 | @:expose 9 | class EngineEvent { 10 | public static function create():EngineEvent { 11 | return new EngineEvent(); 12 | } 13 | 14 | public var playerUpdate:(instances:Array) -> Void; // PLAYER_UPDATE 15 | public var playerMoveStart:(move:PlayerMove) -> Void; // PLAYER_MOVES_START 16 | 17 | public var playerOutOfRange:(list:Array) -> Void; // PLAYER_OUT_OF_RANGE 18 | public var playerName:(id:Int, firstName:String, lastName:String) -> Void; // NAME 19 | 20 | public var apocalypse:() -> Void; // APOCALYPSE 21 | public var apocalypseDone:() -> Void; // APOCALYPSE_DONE 22 | 23 | public var posse:(killer:Int, target:Int) -> Void; // POSSE_JOIN 24 | 25 | public var following:(follower:Int, leader:Int, color:Int) -> Void; // FOLLOWING 26 | public var exiled:(target:Int, id:Int) -> Void; // EXILED 27 | public var cursed:(id:Int, level:Int, word:String) -> Void; // CURSED 28 | public var curseToken:(count:Int) -> Void; // CURSE_TOKEN_CHANGE 29 | public var curseScore:(excess:Int) -> Void; // CURSE_SCORE_CHANGE 30 | 31 | public var badBiomes:(id:Int, name:String) -> Void; // BAD_BIOMES 32 | 33 | public var vogUpdate:() -> Void; // VOG_UPDATE 34 | public var photo:(x:Int, y:Int, signature:String) -> Void; // PHOTO_SIGNATURE 35 | 36 | public var shutdown:() -> Void; // FORCED_SHUTDOWN 37 | 38 | public var global:(text:String) -> Void; // GLOBAL_MESSAGE 39 | public var war:(a:Int, b:Int, status:String) -> Void; // WAR_REPORT 40 | 41 | public var learnedTools:(list:Array) -> Void; // LEARNED_TOOL_REPORT 42 | public var toolExperts:(list:Array) -> Void; // TOOL_EXPERTS 43 | public var toolSlots:(total:Int) -> Void; // TOOL_SLOTS 44 | 45 | public var babyWiggle:(list:Array) -> Void; // BABY_WIGGLE 46 | public var saysLocation:(x:Int, y:Int, text:String) -> Void; // LOCATION_SAYS 47 | public var dying:(id:Int, sick:Bool) -> Void; // DYING 48 | public var says:(id:Int, text:String, curse:Bool) -> Void; // PLAYER_SAYS 49 | public var emot:(id:Int, index:Int, sec:Int) -> Void; // PLAYER_EMOT 50 | 51 | public var mapChunk:(instance:MapInstance) -> Void; // MAP_CHUNK 52 | public var mapChange:(change:MapChange) -> Void; // MAP_CHANGE 53 | public var foodChange:(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int) -> Void; // FOOD_CHANGE 54 | public var heatChange:(heat:Float, foodTime:Float, indoorBonus:Float) -> Void; // HEAT_CHANGE 55 | public var frame:() -> Void; // FRAME 56 | public var lineage:(list:Array, eve:Int) -> Void; // LINEAGE 57 | public var healed:(id:Int) -> Void; // HEALED 58 | public var monument:(x:Int, y:Int, id:Int) -> Void; // MONUMENT_CALL 59 | public var grave:(x:Int, y:Int, id:Int) -> Void; // GRAVE 60 | public var graveOld:(x:Int, y:Int, pid:Int, poid:Int, age:Float, name:String, lineage:Array) -> Void; // GRAVE_OLD 61 | public var graveMove:(xs:Int, ys:Int, xd:Int, yd:Int, swapDest:Bool) -> Void; // GRAVE_MOVE 62 | public var ownerList:(x:Int, y:Int, list:Array) -> Void; // OWNER_LIST 63 | public var valley:(spacing:Int, offset:Int) -> Void; // VALLEY_SPACING 64 | public var flight:(id:Int, x:Int, y:Int) -> Void; // FLIGHT_DEST 65 | public var homeland:(x:Int, y:Int, name:String) -> Void; // HOMELAND 66 | public var craving:(id:Int, bonus:Int) -> Void; // CRAVING 67 | public var flip:(x:Int, y:Int) -> Void; // FLIP 68 | 69 | public function new() {} 70 | } 71 | -------------------------------------------------------------------------------- /openlife/engine/EngineHeader.hx: -------------------------------------------------------------------------------- 1 | package openlife.engine; 2 | 3 | import openlife.data.map.MapInstance; 4 | import openlife.data.object.player.PlayerInstance; 5 | import openlife.data.object.player.PlayerMove; 6 | import openlife.data.map.MapChange; 7 | 8 | // #if nativeGen @:nativeGen #end 9 | @:expose 10 | interface EngineHeader { 11 | // Headers not included: 12 | // COMPRESSED_MESSAGE 13 | public function playerUpdate(instances:Array):Void; // PLAYER_UPDATE 14 | public function playerMoveStart(move:PlayerMove):Void; // PLAYER_MOVES_START 15 | 16 | public function playerOutOfRange(list:Array):Void; // PLAYER_OUT_OF_RANGE 17 | public function playerName(id:Int, firstName:String, lastName:String):Void; // NAME 18 | 19 | public function apocalypse():Void; // APOCALYPSE 20 | public function apocalypseDone():Void; // APOCALYPSE_DONE 21 | 22 | public function posse(killer:Int, target:Int):Void; // POSSE_JOIN 23 | 24 | public function following(follower:Int, leader:Int, color:Int):Void; // FOLLOWING 25 | public function exiled(target:Int, id:Int):Void; // EXILED 26 | public function cursed(id:Int, level:Int, word:String):Void; // CURSED 27 | public function curseToken(count:Int):Void; // CURSE_TOKEN_CHANGE 28 | public function curseScore(excess:Int):Void; // CURSE_SCORE_CHANGE 29 | 30 | public function badBiomes(id:Int, name:String):Void; // BAD_BIOMES 31 | 32 | public function vogUpdate():Void; // VOG_UPDATE 33 | public function photo(x:Int, y:Int, signature:String):Void; // PHOTO_SIGNATURE 34 | 35 | public function shutdown():Void; // FORCED_SHUTDOWN 36 | 37 | public function global(text:String):Void; // GLOBAL_MESSAGE 38 | public function war(a:Int, b:Int, status:String):Void; // WAR_REPORT 39 | 40 | public function learnedTools(list:Array):Void; // LEARNED_TOOL_REPORT 41 | public function toolExperts(list:Array):Void; // TOOL_EXPERTS 42 | public function toolSlots(total:Int):Void; // TOOL_SLOTS 43 | 44 | public function babyWiggle(list:Array):Void; // BABY_WIGGLE 45 | public function saysLocation(x:Int, y:Int, text:String):Void; // LOCATION_SAYS 46 | public function dying(id:Int, sick:Bool):Void; // DYING 47 | public function says(id:Int, text:String, curse:Bool):Void; // PLAYER_SAYS 48 | public function emot(id:Int, index:Int, sec:Int):Void; // PLAYER_EMOT 49 | 50 | public function mapChunk(instance:MapInstance):Void; // MAP_CHUNK 51 | public function mapChange(change:MapChange):Void; // MAP_CHANGE 52 | public function foodChange(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int):Void; // FOOD_CHANGE 53 | public function heatChange(heat:Float, foodTime:Float, indoorBonus:Float):Void; // HEAT_CHANGE 54 | public function frame():Void; // FRAME 55 | public function lineage(list:Array, eve:Int):Void; // LINEAGE 56 | public function healed(id:Int):Void; // HEALED 57 | public function monument(x:Int, y:Int, id:Int):Void; // MONUMENT_CALL 58 | public function grave(x:Int, y:Int, id:Int):Void; // GRAVE 59 | public function graveOld(x:Int, y:Int, pid:Int, poid:Int, age:Float, name:String, lineage:Array):Void; // GRAVE_OLD 60 | public function graveMove(xs:Int, ys:Int, xd:Int, yd:Int, swapDest:Bool):Void; // GRAVE_MOVE 61 | public function ownerList(x:Int, y:Int, list:Array):Void; // OWNER_LIST 62 | public function valley(spacing:Int, offset:Int):Void; // VALLEY_SPACING 63 | public function flight(id:Int, x:Int, y:Int):Void; // FLIGHT_DEST 64 | public function homeland(x:Int, y:Int, name:String):Void; // HOMELAND 65 | public function craving(id:Int, bonus:Int):Void; // CRAVING 66 | public function flip(x:Int, y:Int):Void; // FLIP 67 | } 68 | -------------------------------------------------------------------------------- /openlife/client/ClientTag.hx: -------------------------------------------------------------------------------- 1 | package openlife.client; 2 | 3 | // tags used by the client 4 | @:expose 5 | @:enum abstract ClientTag(Null) { 6 | public var COMPRESSED_MESSAGE = "CM"; 7 | public var MAP_CHUNK = "MC"; 8 | public var PLAYER_UPDATE = "PU"; 9 | public var PLAYER_MOVES_START = "PM"; 10 | public var PLAYER_OUT_OF_RANGE = "PO"; 11 | public var BABY_WIGGLE = "BW"; 12 | public var PLAYER_SAYS = "PS"; 13 | public var LOCATION_SAYS = "LS"; 14 | public var PLAYER_EMOT = "PE"; 15 | public var MAP_CHANGE = "MX"; 16 | public var FOOD_CHANGE = "FX"; 17 | public var HEAT_CHANGE = "HX"; 18 | public var LINEAGE = "LN"; 19 | public var NAME = "NM"; 20 | public var APOCALYPSE = "AP"; 21 | public var APOCALYPSE_DONE = "AD"; 22 | public var DYING = "DY"; 23 | public var HEALED = "HE"; 24 | public var MONUMENT_CALL = "MN"; 25 | public var POSSE_JOIN = "PJ"; 26 | public var GRAVE = "GV"; 27 | public var GRAVE_MOVE = "GM"; 28 | public var GRAVE_OLD = "GO"; 29 | public var OWNER_LIST = "OW"; 30 | public var VALLEY_SPACING = "VS"; 31 | public var CURSED = "CU"; 32 | public var CURSE_TOKEN_CHANGE = "CX"; 33 | public var CURSE_SCORE_CHANGE = "CS"; 34 | public var BAD_BIOMES = "BB"; 35 | public var FLIGHT_DEST = "FD"; 36 | public var VOG_UPDATE = "VU"; 37 | public var PHOTO_SIGNATURE = "PH"; 38 | public var FORCED_SHUTDOWN = "SD"; 39 | public var GLOBAL_MESSAGE = "MS"; 40 | public var WAR_REPORT = "WR"; 41 | public var LEARNED_TOOL_REPORT = "LR"; 42 | public var TOOL_EXPERTS = "TE"; 43 | public var FRAME = "FM"; 44 | public var PONG = "PONG"; 45 | public var FOLLOWING = "FW"; 46 | public var EXILED = "EX"; 47 | public var TOOL_SLOTS = "TS"; 48 | public var HOMELAND = "HL"; 49 | // new 50 | public var ACCEPTED = "ACCEPTED"; 51 | public var REJECTED = "REJECTED"; 52 | public var SERVER_INFO = "SN"; 53 | public var CRAVING = "CR"; 54 | public var FLIP = "FL"; 55 | public var UFOL = "UFOL"; 56 | 57 | @:from private static function fromString(value:String):ClientTag { 58 | // trace("set tag " + value); 59 | return switch (value) { 60 | case "CM": COMPRESSED_MESSAGE; 61 | case "MC": MAP_CHUNK; 62 | case "PU": PLAYER_UPDATE; 63 | case "PM": PLAYER_MOVES_START; 64 | case "PO": PLAYER_OUT_OF_RANGE; 65 | case "BW": BABY_WIGGLE; 66 | case "PS": PLAYER_SAYS; 67 | case "LS": LOCATION_SAYS; 68 | case "PE": PLAYER_EMOT; 69 | case "MX": MAP_CHANGE; 70 | case "FX": FOOD_CHANGE; 71 | case "HX": HEAT_CHANGE; 72 | case "LN": LINEAGE; 73 | case "NM": NAME; 74 | case "AP": APOCALYPSE; 75 | case "AD": APOCALYPSE_DONE; 76 | case "DY": DYING; 77 | case "HE": HEALED; 78 | case "MN": MONUMENT_CALL; 79 | case "PJ": POSSE_JOIN; 80 | case "GV": GRAVE; 81 | case "GM": GRAVE_MOVE; 82 | case "GO": GRAVE_OLD; 83 | case "OW": OWNER_LIST; 84 | case "VS": VALLEY_SPACING; 85 | case "CU": CURSED; 86 | case "CX": CURSE_TOKEN_CHANGE; 87 | case "CS": CURSE_SCORE_CHANGE; 88 | case "FD": FLIGHT_DEST; 89 | case "BB": BAD_BIOMES; 90 | case "VU": VOG_UPDATE; 91 | case "PH": PHOTO_SIGNATURE; 92 | case "SD": FORCED_SHUTDOWN; 93 | case "MS": GLOBAL_MESSAGE; 94 | case "WR": WAR_REPORT; 95 | case "LR": LEARNED_TOOL_REPORT; 96 | case "TE": TOOL_EXPERTS; 97 | case "FW": FOLLOWING; 98 | case "EX": EXILED; 99 | case "FM": FRAME; 100 | case "PONG": PONG; 101 | case "TS": TOOL_SLOTS; 102 | case "HL": HOMELAND; 103 | case "CR": CRAVING; 104 | case "FL": FLIP; 105 | case "UFOL": UFOL; 106 | // new 107 | case "SN": SERVER_INFO; 108 | case "ACCEPTED": ACCEPTED; 109 | case "REJECTED": REJECTED; 110 | default: null; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /openlife/data/LineReader.hx: -------------------------------------------------------------------------------- 1 | package openlife.data; 2 | 3 | // multi platform to read input 4 | import haxe.io.Input; 5 | import haxe.ds.Vector; 6 | 7 | class LineReader { 8 | @:doxHide(false) 9 | /** 10 | * line vector of data 11 | */ 12 | var line:Vector; 13 | @:doxHide(false) 14 | /** 15 | * next 16 | */ 17 | var next:Int = 0; 18 | @:doxHide(false) 19 | var input:Input; 20 | 21 | public static inline var EOL:String = "\n"; 22 | 23 | public function new() {} 24 | 25 | /** 26 | * Read lines and put into line vector 27 | * @param string text split into lines 28 | */ 29 | public function readLines(string:String):Bool { 30 | if (string.length == 0) return false; 31 | next = 0; 32 | line = Vector.fromArrayCopy(string.split(EOL)); 33 | return true; 34 | } 35 | 36 | /** 37 | * Float Array value from line 38 | * @return Array 39 | */ 40 | public function getFloatArray():Array { 41 | var array:Array = []; 42 | for (o in getStringArray()) { 43 | array.push(Std.parseFloat(o)); 44 | } 45 | return array; 46 | } 47 | 48 | /** 49 | * Int Array value from line 50 | * @return Array 51 | */ 52 | public function getIntArray():Array { 53 | var array:Array = []; 54 | for (o in getStringArray()) { 55 | array.push(Std.parseInt(o)); 56 | } 57 | return array; 58 | } 59 | 60 | /** 61 | * String Array value from line 62 | * @return Array 63 | */ 64 | public function getStringArray():Array { 65 | return getString().split(","); 66 | } 67 | 68 | /** 69 | * Point (x,y) from line 70 | * @return Point 71 | */ 72 | public function getPoint():Point { 73 | var string = getString(); 74 | var comma:Int = string.indexOf(","); 75 | return new Point(Std.parseInt(string.substring(0, comma)), Std.parseInt(string.substring(comma + 1, string.length))); 76 | } 77 | 78 | /** 79 | * Boolean value from line 80 | * @return Bool 81 | */ 82 | public function getBool():Bool { 83 | return getString().substr(0, 1) == "1"; 84 | } 85 | 86 | /** 87 | * Int from string 88 | * @return Int 89 | */ 90 | public function getInt():Int { 91 | return Std.parseInt(getString()); 92 | } 93 | 94 | var i:Int = 0; 95 | var j:Int = 0; 96 | var string:String; 97 | 98 | /** 99 | * Multi property array int value from line 100 | * @return Array 101 | */ 102 | public function getArrayInt():Array { 103 | var array:Array = []; 104 | var bool:Bool = true; 105 | string = "=" + line[next++]; 106 | i = 0; 107 | j = 0; 108 | while (bool) { 109 | i = string.indexOf("=", i + 0) + 1; 110 | j = string.indexOf(",", i); 111 | j = j < 0 ? string.length : j; 112 | array.push(Std.parseInt(string.substring(i, j))); 113 | if (j >= string.length - 1) bool = false; 114 | } 115 | array.shift(); // FIX THIS 116 | return array; 117 | } 118 | 119 | /** 120 | * Float value from line 121 | * @return Float 122 | */ 123 | public function getFloat():Float { 124 | return Std.parseFloat(getString()); 125 | } 126 | 127 | /** 128 | * String value from line 129 | * @return String 130 | */ 131 | public function getString():String { 132 | if (next + 1 > line.length) throw 'max $line'; 133 | string = line[next++]; 134 | if (string == null || string == "") return ""; 135 | var equals = string.indexOf("="); 136 | return string.substr(equals + 1); 137 | } 138 | 139 | /** 140 | * Name from line 141 | * @param name 142 | * @return Bool 143 | */ 144 | public function readName(name:String):Bool { 145 | string = line[next]; 146 | if (name == string.substring(0, name.length)) return true; 147 | return false; 148 | } 149 | 150 | public function getName():String { 151 | string = line[next]; 152 | return string.substring(0, string.indexOf("=")); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /openlife/client/BinPack.hx: -------------------------------------------------------------------------------- 1 | import haxe.ds.Vector; 2 | 3 | class BinPack { 4 | public var spaces:Array = []; 5 | 6 | var width:Int; 7 | var height:Int; 8 | 9 | public function new(width:Int, height:Int) { 10 | this.width = width; 11 | this.height = height; 12 | spaces.push({width: width,height: height}); 13 | } 14 | 15 | /** 16 | size = width, height 17 | **/ 18 | public function pack(size:Rect):Rect { 19 | // width, height 20 | spaces.sort(function(a, b) { 21 | return a.width * a.height > b.width * b.height ? 1 : -1; 22 | }); 23 | for (space in spaces) { 24 | // 2 = width, height = 3, image is bigger than space 25 | if (space.width < size.width || space.height < size.height) 26 | continue; 27 | // size fits exactly 28 | if (space.width == size.width && space.height == size.height) { 29 | spaces.remove(space); 30 | return space; 31 | } 32 | if (space.width == size.width && space.height > size.height) { 33 | // width fits exactly, height does not 34 | spaces.push({ 35 | width: space.width, // width 36 | height: space.height - size.height, // height 37 | x: space.x, // x 38 | y: space.y + size.height, // y 39 | }); 40 | spaces.remove(space); 41 | // width, height, x, y 42 | return {width: size.width, height: size.height, x: space.x, y: space.y}; 43 | } 44 | if (space.height == size.height && space.width > size.width) { 45 | // height fits exactly, width does not 46 | spaces.push({ 47 | width: space.width - size.width, // width 48 | height: space.height, // height 49 | x: space.x + size.width, // x 50 | y: space.y, // y 51 | }); 52 | spaces.remove(space); 53 | return {width: size.width, height: size.height, x: space.x, y: space.y}; 54 | } 55 | // every other option gone, size is strictly smaller than space both width and height 56 | if (space.width - size.width > space.height - size.height) { 57 | // width has more space 58 | spaces.push({ 59 | width: space.width - size.width, // space.width - size.width = width 60 | height: space.height, // space.height = height 61 | x: space.x + size.width, // space.x + size.width = x 62 | y: space.y // space.y = y 63 | }); 64 | spaces.push({ 65 | width: size.width, // width 66 | height: space.height - size.height, // height 67 | x: space.x, // x 68 | y: space.y + size.height // y 69 | }); 70 | } else { 71 | // height has more space 72 | spaces.push({ 73 | width: space.width, // width 74 | height: space.height - size.height, // height 75 | x: space.x, // x 76 | y: space.y + size.height // y 77 | }); 78 | spaces.push({ 79 | width: space.width - size.width, // space.width - size.width = width 80 | height: size.height, // space.height = height 81 | x: space.x + size.width, // space.x + size.width = x 82 | y: space.y // space.y = y 83 | }); 84 | } 85 | spaces.remove(space); 86 | return {width: size.width, height: size.height, x: space.x, y: space.y}; 87 | } 88 | return null; 89 | } 90 | 91 | public function volumeLeft():Int { 92 | var left = width * height; 93 | for (space in spaces) { 94 | left -= space.width * space.height; 95 | } 96 | return left; 97 | } 98 | } 99 | 100 | @:structInit 101 | class Rect { 102 | public var x:Int = 0; 103 | public var y:Int = 0; 104 | public var width:Int = 0; 105 | public var height:Int = 0; 106 | public var maxX(get, never):Int; 107 | public var maxY(get, never):Int; 108 | 109 | function get_maxX():Int 110 | return x + width; 111 | function get_maxY():Int 112 | return y + height; 113 | 114 | public inline function new(x=0,y=0,width,height) { 115 | this.x = x; 116 | this.y = y; 117 | this.width = width; 118 | this.height = height; 119 | } 120 | public function toString() 121 | return '$x,$y,$width,$height'; 122 | } -------------------------------------------------------------------------------- /openlife/graphics/TgaData.hx: -------------------------------------------------------------------------------- 1 | package openlife.graphics; 2 | 3 | import format.tga.Tools; 4 | import haxe.ds.Vector; 5 | import haxe.io.Output; 6 | import haxe.io.Bytes; 7 | import openlife.data.Rectangle; 8 | import haxe.io.BytesInput; 9 | import format.tga.Data; 10 | import format.tga.Reader; 11 | import format.tga.Writer; 12 | 13 | @:expose 14 | class TgaData { 15 | public var bytes:Bytes; 16 | public var rect:Rectangle; 17 | 18 | // to read 19 | private var r:Reader; 20 | 21 | public var data:Data; 22 | 23 | public function new() {} 24 | 25 | public function read(bytes:Bytes, extractBool:Bool = true) { 26 | r = new Reader(new BytesInput(bytes, 0, bytes.length)); 27 | this.data = r.read(); 28 | if (extractBool) extract(); 29 | } 30 | 31 | public function extract() { 32 | rect = new Rectangle(0, 0, data.header.width, data.header.height); 33 | this.bytes = Tools.extract32(data, alpha); 34 | } 35 | 36 | public function write(data:Data, output:Output) { 37 | var w:Writer = new Writer(output); 38 | w.write(data); 39 | output.close(); 40 | } 41 | 42 | public function bytesToVector(bytes:Bytes):Vector { 43 | var imageData:Vector = new Vector(Std.int(bytes.length / 4)); 44 | for (i in 0...imageData.length) { 45 | imageData[i] = bytes.getInt32(i); 46 | } 47 | return imageData; 48 | } 49 | 50 | /** 51 | * crop alpha parts from the sides 52 | */ 53 | public var alpha:Bool = true; 54 | 55 | public function crop() { 56 | // shifting vars 57 | var minX:Int = getLeft(0, Std.int(data.header.width / 2), 0, data.header.height) - padding; 58 | var maxX:Int = getRight(0, data.header.width, 0, data.header.height) + padding; 59 | 60 | var minY:Int = getTop(0, data.header.width, 0, Std.int(data.header.height / 2)) - padding; 61 | var maxY:Int = getBottom(0, data.header.width, 0, data.header.height) + padding; 62 | // if padding is to much 63 | if (minX < 0) minX = 0; 64 | if (minY < 0) minY = 0; 65 | if (maxX > data.header.width) maxX = data.header.width; 66 | if (maxY > data.header.height) maxY = data.header.height; 67 | // create vector of image pixels 68 | var vector = new Vector((maxX - minX) * (maxY - minY)); 69 | var index:Int = 0; 70 | var i:Int = 0; 71 | for (y in minY...maxY) { 72 | for (x in minX...maxX) { 73 | vector[index++] = data.imageData[x + y * data.header.width]; 74 | } 75 | } 76 | data.imageData = vector; 77 | data.header.width = maxX - minX; 78 | data.header.height = maxY - minY; 79 | extract(); 80 | } 81 | 82 | private static inline var threshold:Int = 3 * 255; 83 | private static inline var padding:Int = 15; 84 | 85 | private function getLeft(x0:Int, x1:Int, y0:Int, y1:Int):Int { 86 | // x 87 | for (i in x0...x1) { 88 | // y 89 | for (j in y0...y1) { 90 | if ((data.imageData[i + j * data.header.width] >> 24) & 0xff < threshold) { 91 | return i - 1; 92 | } 93 | } 94 | } 95 | return 0; 96 | } 97 | 98 | private function getRight(x0:Int, x1:Int, y0:Int, y1:Int):Int { 99 | // x 100 | var x:Int = x1; 101 | while (x > x0) { 102 | // y 103 | for (j in y0...y1) { 104 | if ((data.imageData[x + j * data.header.width] >> 24) & 0xff < threshold) { 105 | return x + 1; 106 | } 107 | } 108 | x--; 109 | } 110 | return 0; 111 | } 112 | 113 | private function getBottom(x0:Int, x1:Int, y0:Int, y1:Int, rev:Bool = false):Int { 114 | // y 115 | var y:Int = y1; 116 | while (y > y0) { 117 | // x 118 | for (i in x0...x1) { 119 | if ((data.imageData[i + y * data.header.width] >> 24) & 0xff < threshold) { 120 | return y + 1; 121 | } 122 | } 123 | y--; 124 | } 125 | return 0; 126 | } 127 | 128 | private function getTop(x0:Int, x1:Int, y0:Int, y1:Int, rev:Bool = false):Int { 129 | // y 130 | for (j in y0...y1) { 131 | // x 132 | for (i in x0...x1) { 133 | if ((data.imageData[i + j * data.header.width] >> 24) & 0xff < threshold) { 134 | return j - 1; 135 | } 136 | } 137 | } 138 | return 0; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /scripts/Transition.hx: -------------------------------------------------------------------------------- 1 | import openlife.data.transition.TransitionData; 2 | import openlife.data.object.ObjectData; 3 | import haxe.ds.Vector; 4 | import openlife.resources.ObjectBake; 5 | import openlife.engine.Utility; 6 | import openlife.engine.Engine; 7 | import openlife.data.transition.TransitionImporter; 8 | 9 | class Transition { 10 | public static function main() { 11 | Engine.dir = Utility.dir(); 12 | new Transition(); 13 | } 14 | 15 | var catMap:Map>; 16 | var transMap:Map>; 17 | var blacklisted:Array = []; 18 | 19 | private function new() { 20 | var importer = new TransitionImporter(); 21 | trace("object list"); 22 | var vector = ObjectBake.objectList(); 23 | blacklisted = [50, 51, 52]; // add milkweed as it's causing errors 24 | for (id in vector) { 25 | var obj = new ObjectData(id); 26 | if (obj.isNatural() || obj.numUses > 1) { 27 | blacklisted.push(id); 28 | } 29 | } 30 | trace("categories"); 31 | importer.importCategories(); 32 | catMap = new Map>(); 33 | for (cat in importer.categories) { 34 | if (cat.pattern) continue; 35 | cat.ids.sort(function(a:Int, b:Int) { 36 | return a > b ? 1 : -1; 37 | }); 38 | catMap.set(cat.parentID, cat.ids); 39 | } 40 | importer.categories = []; 41 | trace("transitions"); 42 | importer.importTransitions(); 43 | transMap = new Map>(); 44 | for (trans in importer.transitions) { 45 | if (trans.actorID != trans.newActorID) add(trans.newActorID, trans); 46 | if (trans.targetID != trans.newTargetID) add(trans.newTargetID, trans); 47 | } 48 | while (true) { 49 | trace("id:"); 50 | var id = Std.parseInt(Sys.stdin().readLine()); 51 | var obj = transMap.get(id); 52 | if (obj != null) { 53 | sort(obj); 54 | trace(obj[0] + " possibilities " + obj.length); 55 | var stepsArray:Array = []; 56 | steps(obj[0], stepsArray); 57 | trace("steps: " + stepsArray.length); 58 | /*for (o in obj) 59 | { 60 | Sys.println(o); 61 | //trace("depth: " + depth(o)); 62 | var stepsArray:Array = []; 63 | steps(o,stepsArray); 64 | trace("steps " + stepsArray.length); 65 | }*/ 66 | } else { 67 | trace("trans null"); 68 | } 69 | } 70 | } 71 | 72 | private inline function get(id:Int):Array { 73 | return catMap.exists(id) ? catMap.get(id) : [id]; 74 | } 75 | 76 | private inline function add(id:Int, trans:TransitionData) { 77 | if (transMap.exists(trans.targetID) 78 | && (transMap.get(trans.targetID)[0].target[0] == id || transMap.get(trans.targetID)[0].actor[0] == id)) return; 79 | if (transMap.exists(trans.actorID) 80 | && (transMap.get(trans.actorID)[0].actor[0] == id || transMap.get(trans.actorID)[0].target[0] == id)) return; 81 | if (blacklisted.indexOf(id) != -1) return; 82 | // limit only objects 83 | if (id <= 0) return; 84 | // go through all add add potential list of objects in formula 85 | for (id in get(id)) { 86 | if (id == trans.targetID || id == trans.actorID) continue; 87 | if (!transMap.exists(id)) transMap.set(id, []); 88 | var array = transMap.get(id); 89 | var a = get(trans.actorID); 90 | var b = get(trans.targetID); 91 | array.unshift({ 92 | actor: a, 93 | target: b, 94 | tool: trans.tool, 95 | decay: Math.round(trans.autoDecaySeconds) 96 | }); 97 | } 98 | } 99 | 100 | private inline function steps(node:NodeData, array:Array, count:Int = 0) { 101 | // if (++count > 30) return; 102 | var actorId = node.actor[0]; 103 | var targetId = node.target[0]; 104 | var actor = transMap.get(actorId); 105 | var target = transMap.get(targetId); 106 | Sys.sleep(0.5); 107 | trace(node); 108 | if (actor != null) { 109 | steps(actor[0], array, count); 110 | } 111 | if (target != null) { 112 | steps(target[0], array, count); 113 | } 114 | array.push(node); 115 | } 116 | 117 | private inline function sort(nodes:Array) { 118 | nodes.sort(function(a:NodeData, b:NodeData) { 119 | if (a.tool && !b.tool) return 1; 120 | if (!a.tool && b.tool) return -1; 121 | return (a.actor[0] + a.target[0]) >= (b.actor[0] + b.target[0]) ? 1 : -1; 122 | }); 123 | } 124 | } 125 | 126 | typedef NodeData = {target:Array, actor:Array, tool:Bool, decay:Int} 127 | -------------------------------------------------------------------------------- /openlife/data/animation/AnimationData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.animation; 2 | 3 | import haxe.ds.Vector; 4 | import openlife.data.sound.SoundData; 5 | import openlife.resources.Resource; 6 | 7 | @:expose 8 | class AnimationData extends openlife.data.LineReader { 9 | /** 10 | * If animation failed to load 11 | */ 12 | public var fail:Bool = false; 13 | 14 | /** 15 | * Records of animation 16 | */ 17 | @:keep public var record:Vector; 18 | 19 | public function new(id:Int) { 20 | super(); 21 | // fail = true; 22 | record = new Vector(5 + 1); 23 | for (i in 0...5 + 1) { 24 | // skip 3 25 | if (i == 3) continue; 26 | // read lines 27 | if (!readLines(Resource.animation(id, i))) return; 28 | record[i] = process(); 29 | } 30 | line = null; 31 | } 32 | 33 | /** 34 | * Process animation 35 | * @return AnimationRecord 36 | */ 37 | public function process():AnimationRecord { 38 | // id 39 | var animation = new AnimationRecord(); 40 | animation.id = getInt(); 41 | // type 42 | var string = getString(); 43 | var cut:Int = string.indexOf(","); 44 | var sep = string.indexOf(":"); 45 | if (animation.type == moving) trace("moving process"); 46 | if (sep == -1) { 47 | sep = cut; 48 | } else { 49 | // extra index read 50 | } 51 | switch (Std.parseInt(string.substring(0, sep))) { 52 | case 0: 53 | animation.type = ground; 54 | case 1: 55 | animation.type = held; 56 | case 2: 57 | animation.type = moving; 58 | case 3: 59 | animation.type = eating; 60 | case 4: 61 | animation.type = doing; 62 | case 5: 63 | animation.type = endAnimType; 64 | } 65 | // rand start phase 66 | animation.randStartPhase = Std.parseFloat(string.substring(cut + 1, string.length)); 67 | 68 | if (readName("forceZeroStart")) { 69 | next++; 70 | } 71 | // next++; 72 | // num 73 | if (readName("numSounds")) { 74 | animation.numSounds = getInt(); 75 | // skip over sounds 76 | if (animation.numSounds > 0) { 77 | animation.soundAnim = new Vector(animation.numSounds); 78 | for (i in 0...animation.numSounds) { 79 | animation.soundAnim[i] = new SoundParameter(); 80 | var array = getString().split("#"); 81 | var index = array[array.length - 1].indexOf(" "); 82 | array[array.length - 1] = array[array.length - 1].substring(0, index); 83 | var propString = array[array.length - 1].substring(index + 1, array[array.length - 1].length); 84 | animation.soundAnim[i].sounds = new Vector(array.length); 85 | for (j in 0...array.length) { 86 | animation.soundAnim[i].sounds[j] = new SoundData(array[j]); 87 | } 88 | array = propString.split(" "); 89 | animation.soundAnim[i].repeatPerSec = Std.parseFloat(array[0]); 90 | animation.soundAnim[i].repeatPhase = Std.parseFloat(array[1]); 91 | animation.soundAnim[i].ageStart = Std.parseFloat(array[2]); 92 | animation.soundAnim[i].ageEnd = Std.parseFloat(array[3]); 93 | } 94 | } 95 | } 96 | animation.numSprites = getInt(); 97 | animation.numSlots = getInt(); 98 | // sprites 99 | #if neko 100 | if (animation.numSprites == null) return animation; 101 | #end 102 | if (animation.numSprites <= 0) return animation; 103 | animation.params = new Vector(animation.numSprites); 104 | for (i in 0...animation.params.length) 105 | animation.params[i] = processParam(); 106 | // slots 107 | animation.slotAnim = new Vector(animation.numSlots); 108 | for (i in 0...animation.slotAnim.length) 109 | animation.slotAnim[i] = processParam(); 110 | // done 111 | return animation; 112 | } 113 | 114 | /** 115 | * Process the paramaters of record 116 | * @return AnimationParameter 117 | */ 118 | public function processParam():AnimationParameter { 119 | var param:AnimationParameter = new AnimationParameter(); 120 | if (readName("offset")) { 121 | var s:String = getString(); 122 | var cut = s.indexOf(","); 123 | param.offsetX = Std.parseFloat(s.substring(1, cut)); 124 | param.offsetY = Std.parseFloat(s.substring(cut + 1, s.length - 1)); 125 | } 126 | if (readName("startPause")) { 127 | param.startPauseSec = getFloat(); 128 | } 129 | // animation param 130 | var animParam = getString(); 131 | param.process(animParam.split(" ")); 132 | return param; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /openlife/server/ScoreEntry.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | import openlife.data.object.ObjectHelper; 4 | import openlife.settings.ServerSettings; 5 | 6 | // TODO save to disk 7 | class ScoreEntry { 8 | public var accountId:Int; 9 | public var playerId:Int; 10 | public var relativeAccountId:Int; 11 | public var relativePlayerId:Int; 12 | public var score:Float = 0; 13 | public var text:String; 14 | 15 | public function new() {} 16 | 17 | public static function CreateScoreEntryIfGrave(decayedObj:ObjectHelper) { 18 | if (decayedObj.id != 89) return; // Old Grave 19 | 20 | var account = decayedObj.getOwnerAccount(); 21 | var creator = decayedObj.getCreator(); 22 | 23 | if (account == null) return; 24 | 25 | var score = new ScoreEntry(); 26 | score.accountId = account.id; 27 | score.playerId = creator != null ? creator.p_id : 0; 28 | score.score = -ServerSettings.OldGraveDecayMali; 29 | score.text = creator != null ? 'No one burried ${creator.name} ${creator.familyName}!' : 'No one burried your old bones!'; 30 | account.scoreEntries.push(score); 31 | } 32 | 33 | public static function CreateScoreEntryForCursedGrave(cursedGrave:ObjectHelper) { 34 | var account = cursedGrave.getOwnerAccount(); 35 | var creator = cursedGrave.getCreator(); 36 | var creatorId = cursedGrave.getCreatorId(); 37 | 38 | if (account == null) return; 39 | 40 | var scoreEntry = null; 41 | 42 | if (creatorId > 0) { 43 | for (entry in account.scoreEntries) { 44 | if (entry.playerId == creatorId) { 45 | scoreEntry = entry; 46 | break; 47 | } 48 | } 49 | } 50 | 51 | if (scoreEntry == null) { 52 | scoreEntry = new ScoreEntry(); 53 | scoreEntry.accountId = account.id; 54 | scoreEntry.playerId = creator != null ? creator.p_id : 0; 55 | scoreEntry.text = creator != null ? '${creator.name} ${creator.familyName} bones where cursed!' : 'Your old bones where cursed!'; 56 | account.scoreEntries.push(scoreEntry); 57 | } 58 | 59 | scoreEntry.score -= ServerSettings.CursedGraveMali; 60 | } 61 | 62 | public static function CreateScoreEntryForDeadRelative(player:GlobalPlayerInstance) { 63 | // TODO father 64 | // TODO sisters / brothers 65 | if (player.prestige < 10) return; 66 | if (player.mother == null) return; 67 | 68 | var ancestor = player.lineage.getMotherLineage(); 69 | 70 | for (i in 0...10) { 71 | var nextAncestor = ancestor.getMotherLineage(); 72 | 73 | if (nextAncestor != null && (player.account == ancestor.account || WorldMap.calculateRandomFloat() > 0.1)) { 74 | ancestor = nextAncestor; 75 | continue; 76 | } 77 | 78 | if (player.account == ancestor.account) return; 79 | if (ancestor.grave == null || ancestor.grave.isBoneGrave()) return; 80 | 81 | ancestor.account.scoreEntries.push(CreateNewScoreEntry(player, ancestor)); 82 | return; 83 | } 84 | 85 | if (player.account == ancestor.account) return; 86 | if (ancestor.grave == null || ancestor.grave.isBoneGrave()) return; 87 | 88 | ancestor.account.scoreEntries.push(CreateNewScoreEntry(player, ancestor)); 89 | } 90 | 91 | private static function CreateNewScoreEntry(player:GlobalPlayerInstance, ancestor:Lineage) { 92 | var score = new ScoreEntry(); 93 | score.accountId = ancestor.accountId; 94 | score.playerId = ancestor.myId; 95 | score.relativeAccountId = player.account.id; 96 | score.relativePlayerId = player.p_id; 97 | score.score = player.prestige * ServerSettings.AncestorPrestigeFactor; 98 | score.text = '${player.name} ${player.familyName}!'; 99 | 100 | return score; 101 | } 102 | 103 | public static function ProcessScoreEntry(player:GlobalPlayerInstance) { 104 | if (Std.int(player.trueAge) % 5 != 0) return; 105 | if (player.account.scoreEntries.length < 1) return; 106 | 107 | var scoreEntry = player.account.scoreEntries.shift(); 108 | var score = scoreEntry.score; 109 | 110 | if (score < 0 && player.prestige < 10) { 111 | player.account.scoreEntries.push(scoreEntry); 112 | return; 113 | } 114 | 115 | if (score < -20) { 116 | score = -10; 117 | scoreEntry.score += 10; 118 | player.account.scoreEntries.push(scoreEntry); 119 | } 120 | 121 | var tmpScore = Math.round(scoreEntry.score); 122 | var message = scoreEntry.score > 0 ? 'You gained ${tmpScore} prestige from your offsprings ${scoreEntry.text} life' : 'You lost ${- tmpScore} prestige from ${scoreEntry.text}'; 123 | 124 | player.addPrestige(scoreEntry.score); 125 | 126 | player.connection.sendGlobalMessage(message); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tests/Node.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openlife.engine.EngineHeader; 4 | import openlife.engine.Engine; 5 | import openlife.data.object.player.PlayerInstance; 6 | import openlife.data.object.player.PlayerMove; 7 | import openlife.data.map.MapInstance; 8 | import openlife.data.map.MapChange; 9 | 10 | class Node implements EngineHeader { 11 | static function main() { 12 | new Node(); 13 | } 14 | 15 | var engine:Engine; 16 | var player:PlayerInstance; 17 | 18 | function new() { 19 | engine = Engine.create(this); 20 | engine.client.config = { 21 | ip: "localhost" 22 | }; 23 | engine.client.onClose = function() {} 24 | engine.relayPort = 8000; 25 | engine.connect(false, true); 26 | /*var timer = new haxe.Timer(200); 27 | timer.run = function() 28 | { 29 | //trace("tick"); 30 | engine.client.update(); 31 | }*/ 32 | } 33 | 34 | public function playerUpdate(instances:Array) { 35 | trace("PU"); 36 | if (player == null) { 37 | engine.program.setPlayer(player = instances.pop()); 38 | } else { 39 | for (instance in instances) { 40 | if (player.p_id == instance.p_id) player.update(instance); 41 | } 42 | } 43 | engine.program.update(player); 44 | } // PLAYER_UPDATE 45 | 46 | public function playerMoveStart(move:PlayerMove) {} // PLAYER_MOVES_START 47 | 48 | public function playerOutOfRange(list:Array) {} // PLAYER_OUT_OF_RANGE 49 | 50 | public function playerName(id:Int, firstName:String, lastName:String) {} // NAME 51 | 52 | public function apocalypse() {} // APOCALYPSE 53 | 54 | public function apocalypseDone() {} // APOCALYPSE_DONE 55 | 56 | public function posse(killer:Int, target:Int) {} // POSSE_JOIN 57 | 58 | public function following(follower:Int, leader:Int, color:Int) {} // FOLLOWING 59 | 60 | public function exiled(target:Int, id:Int) {} // EXILED 61 | 62 | public function cursed(id:Int, level:Int, word:String) {} // CURSED 63 | 64 | public function curseToken(count:Int) {} // CURSE_TOKEN_CHANGE 65 | 66 | public function curseScore(excess:Int) {} // CURSE_SCORE_CHANGE 67 | 68 | public function badBiomes(id:Int, name:String) {} // BAD_BIOMES 69 | 70 | public function vogUpdate() {} // VOG_UPDATE 71 | 72 | public function photo(x:Int, y:Int, signature:String) {} // PHOTO_SIGNATURE 73 | 74 | public function shutdown() {} // FORCED_SHUTDOWN 75 | 76 | public function global(text:String) {} // GLOBAL_MESSAGE 77 | 78 | public function war(a:Int, b:Int, status:String) {} // WAR_REPORT 79 | 80 | public function learnedTools(list:Array) {} // LEARNED_TOOL_REPORT 81 | 82 | public function toolExperts(list:Array) {} // TOOL_EXPERTS 83 | 84 | public function toolSlots(total:Int) {} // TOOL_SLOTS 85 | 86 | public function babyWiggle(list:Array) {} // BABY_WIGGLE 87 | 88 | public function saysLocation(x:Int, y:Int, text:String) {} // LOCATION_SAYS 89 | 90 | public function dying(id:Int, sick:Bool) {} // DYING 91 | 92 | public function says(id:Int, text:String, curse:Bool) { 93 | trace("SAID"); 94 | engine.program.goto(0, 0); 95 | } // PLAYER_SAYS 96 | 97 | public function emot(id:Int, index:Int, sec:Int) {} // PLAYER_EMOT 98 | 99 | public function mapChunk(instance:MapInstance) { 100 | trace("map " + instance); 101 | } // MAP_CHUNK 102 | 103 | public function mapChange(change:MapChange) {} // MAP_CHANGE 104 | 105 | public function foodChange(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int) {} // FOOD_CHANGE 106 | 107 | public function heatChange(heat:Float, foodTime:Float, indoorBonus:Float) {} // HEAT_CHANGE 108 | 109 | public function frame() {} // FRAME 110 | 111 | public function lineage(list:Array, eve:Int) {} // LINEAGE 112 | 113 | public function healed(id:Int) {} // HEALED 114 | 115 | public function monument(x:Int, y:Int, id:Int) {} // MONUMENT_CALL 116 | 117 | public function grave(x:Int, y:Int, id:Int) {} // GRAVE 118 | 119 | public function graveOld(x:Int, y:Int, pid:Int, poid:Int, age:Float, name:String, lineage:Array) {} // GRAVE_OLD 120 | 121 | public function graveMove(xs:Int, ys:Int, xd:Int, yd:Int, swapDest:Bool) {} // GRAVE_MOVE 122 | 123 | public function ownerList(x:Int, y:Int, list:Array) {} // OWNER_LIST 124 | 125 | public function valley(spacing:Int, offset:Int) {} // VALLEY_SPACING 126 | 127 | public function flight(id:Int, x:Int, y:Int) {} // FLIGHT_DEST 128 | 129 | public function homeland(x:Int, y:Int, name:String) {} // HOMELAND 130 | 131 | public function craving(id:Int, bonus:Int) {} // CRAVING 132 | 133 | public function flip(x:Int, y:Int) {} // FLIP 134 | } 135 | -------------------------------------------------------------------------------- /openlife/client/Object.hx: -------------------------------------------------------------------------------- 1 | import SpriteBatch.BatchElement; 2 | import h2d.col.Bounds; 3 | import openlife.data.animation.AnimationData; 4 | import openlife.data.animation.AnimationRecord; 5 | import openlife.data.object.ObjectData; 6 | 7 | class Object { 8 | public var sprites:Array = []; 9 | public var id:Int; 10 | public var data:ObjectData; 11 | public var anim:AnimationData; 12 | public var x:Float = 0; 13 | public var y:Float = 0; 14 | public var workingSpriteFades:Map = []; 15 | public var bounds:Bounds = new Bounds(); 16 | 17 | public function new(x, y, data) { 18 | this.x = x; 19 | this.y = y; 20 | this.data = data; 21 | try { 22 | anim = new AnimationData(data.id); 23 | } catch (_) {} 24 | } 25 | 26 | public function updateBounds() { 27 | bounds.empty(); 28 | for (sprite in sprites) { 29 | final sx = sprite.x + sprite.t.dx / 2; 30 | final sy = sprite.y + sprite.t.dy / 2; 31 | bounds.addPos(sx, sy); 32 | bounds.addPos(sx + sprite.t.width / 2, sy + sprite.t.height / 2); 33 | } 34 | } 35 | 36 | var inFrameTime = 0.0; 37 | 38 | public function update(dt:Float) { 39 | if (anim == null) return; 40 | final record = anim.record[2]; 41 | 42 | for (i in 0...record.params.length) { 43 | final param = record.params[i]; 44 | final sprite = sprites[i]; 45 | final sprite = sprites[i]; 46 | sprite.x = x + data.spriteArray[i].x; 47 | sprite.y = y - data.spriteArray[i].y; 48 | sprite.rotation = -data.spriteArray[i].rot * 2 * Math.PI; 49 | sprite.x += getOscOffset(inFrameTime, param.offsetX, param.xOscPerSec, param.xAmp, param.xPhase); 50 | sprite.y += -getOscOffset(inFrameTime, param.offsetY, param.yOscPerSec, param.yAmp, param.yPhase); 51 | sprite.rotation += getOscOffset(inFrameTime, 0, param.rockOscPerSec, param.rockAmp, param.rockPhase) * 2 * Math.PI; 52 | final totalRotOffset = param.rotPerSec * inFrameTime + param.rotPhase; 53 | 54 | final sinVal = getOscOffset(inFrameTime, 0, param.fadeOscPerSec, 1, param.fadePhase + 0.25); 55 | 56 | final hardness = param.fadeHardness; 57 | var hardVersion = 0.0; 58 | 59 | if (hardness == 1) { 60 | hardVersion = sinVal > 0 ? 1 : -1; 61 | } else { 62 | var absSinVal = Math.abs(sinVal); 63 | hardVersion = absSinVal != 0 ? (sinVal / absSinVal) * Math.pow(absSinVal, 1 / (hardness * 10 + 1)) : 0; 64 | } 65 | 66 | final fade = (param.fadeMax - param.fadeMin) * (0.5 * hardVersion + 0.5) + param.fadeMin; 67 | 68 | sprite.alpha = fade; 69 | 70 | // relative to 0 on circle 71 | var relativeRotOffset = totalRotOffset - Math.floor(totalRotOffset); 72 | 73 | // make positive 74 | if (relativeRotOffset < 0) { 75 | relativeRotOffset += 1; 76 | } 77 | 78 | // to take average of two rotations 79 | // rotate them both so that one is at 0.5, 80 | // take average, and then rotate them back 81 | // This ensures that we always move through closest average 82 | // point on circle (shortest path along circle). 83 | 84 | var offset = 0.5 - relativeRotOffset; 85 | 86 | sprite.rotation += relativeRotOffset * Math.PI * 2; 87 | } 88 | for (i in 0...record.params.length) { 89 | if (data.spriteArray[i].parent != -1) continue; 90 | transformChild(i, record); 91 | } 92 | inFrameTime += dt / 2; 93 | } 94 | 95 | private function transformChild(parentId:Int, record:AnimationRecord) { 96 | for (i in 0...record.params.length) { 97 | if (data.spriteArray[i].parent != parentId) continue; 98 | final parent = sprites[parentId]; 99 | final parentData = data.spriteArray[parentId]; 100 | final childData = data.spriteArray[i]; 101 | final child = sprites[i]; 102 | child.x += parent.x - x - parentData.x; 103 | child.y += parent.y - y + parentData.y; 104 | 105 | final rot = parent.rotation - parentData.rot * Math.PI * 2; 106 | child.rotation += rot; 107 | rotateTransform(child, parent, rot); 108 | 109 | transformChild(i, record); 110 | } 111 | } 112 | 113 | private inline function rotateTransform(child:BatchElement, parent:BatchElement, rot:Float) { 114 | child.x += -parent.x; 115 | child.y += -parent.y; 116 | 117 | final s = Math.sin(rot); 118 | final c = Math.cos(rot); 119 | 120 | final x = child.x * c - child.y * s; 121 | final y = child.x * s + child.y * c; 122 | 123 | child.x = x + parent.x; 124 | child.y = y + parent.y; 125 | } 126 | 127 | private inline function getOscOffset(inFrameTime:Float, inOffset:Float, inOscPerSec:Float, inAmp:Float, inPhase:Float) 128 | return inOffset + inAmp * Math.sin((inFrameTime * inOscPerSec + inPhase) * 2 * Math.PI); 129 | } 130 | -------------------------------------------------------------------------------- /openlife/auto/PlayerInterface.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | import openlife.server.Lineage; 4 | import haxe.ds.Vector; 5 | import openlife.data.Pos; 6 | import openlife.data.object.ObjectData; 7 | import openlife.data.object.ObjectHelper; 8 | import openlife.data.object.player.PlayerInstance; 9 | import openlife.server.PlayerAccount; 10 | 11 | interface PlayerInterface { 12 | public function getAi():AiBase; 13 | public function getWorld():WorldInterface; 14 | public function getPlayerInstance():PlayerInstance; 15 | 16 | public function doEmote(id:Int, seconds:Int = -10):Void; 17 | public function say(text:String, toSelf:Bool = false):Void; 18 | // public function eat(); 19 | public function self(x:Int = 0, y:Int = 0, clothingSlot:Int = -1):Void; // for eating and clothing 20 | public function move(x:Int, y:Int, seq:Int, moves:Array):Void; 21 | public function remove(x:Int, y:Int, index:Int = -1):Bool; 22 | public function specialRemove(x:Int, y:Int, clothingSlot:Int, index:Null):Bool; 23 | public function use(x:Int, y:Int, containerIndex:Int = -1, target:Int = 0):Bool; 24 | public function drop(x:Int, y:Int, clothingIndex:Int = -1):Bool; 25 | public function dropPlayer(x:Int, y:Int):Bool; 26 | public function doOnOther(x:Int, y:Int, clothingSlot:Int, playerId:Int):Bool; // UBABY 27 | public function doBaby(x:Int, y:Int, playerId:Int):Bool; 28 | public function jump():Bool; 29 | public function kill(x:Int, y:Int, playerId:Int):Bool; // playerId = -1 if no specific player is slected 30 | 31 | // variables 32 | public var id(get, null):Int; 33 | public var name(get, null):String; 34 | public var familyName(get, null):String; 35 | public var lineage(get, null):Lineage; 36 | public var account(get, null):PlayerAccount; 37 | public var x(default, default):Int; 38 | public var y(default, default):Int; 39 | public var gx(default, default):Int; 40 | public var gy(default, default):Int; 41 | public var tx(get, null):Int; 42 | public var ty(get, null):Int; 43 | 44 | public var food_store(default, default):Float; 45 | public var food_store_max(default, default):Float; 46 | public var age(default, default):Float; 47 | public var hits(default, default):Float; 48 | public var heat(default, default):Float; 49 | 50 | public var mother(get, null):PlayerInterface; 51 | public var heldObject(default, default):ObjectHelper; 52 | public var hiddenWound(default, default):ObjectHelper; 53 | public var home(default, default):ObjectHelper; 54 | public var forceStopOnNextTile(default, default):Bool; 55 | 56 | public var blockActorForAi(default, default):ObjectHelper; 57 | public var blockTargetForAi(default, default):ObjectHelper; 58 | 59 | public var clothingObjects(default, default):Vector; 60 | 61 | public var message(default, default):String; 62 | 63 | public function isDeleted():Bool; 64 | public function isHuman():Bool; 65 | public function isAi():Bool; 66 | public function isFemale():Bool; 67 | public function isMale():Bool; 68 | public function isFertile():Bool; 69 | public function isMoving():Bool; 70 | public function isWounded():Bool; 71 | public function isHoldingObject():Bool; 72 | public function isHoldingWeapon():Bool; 73 | public function isAngryOrTerrified():Bool; 74 | public function isBlocked(tx:Int, ty:Int):Bool; 75 | public function isEveOrAdam():Bool; 76 | public function isIll():Bool; 77 | public function isAnimalDeadlyForMe(animal:ObjectHelper, checkIfAnimal:Bool = true):Bool; 78 | public function isAnimalNotDeadlyForMe(animal:ObjectHelper, checkIfAnimal:Bool = true):Bool; 79 | 80 | public function isHeldEmpty():Bool; 81 | public function isHoldingYum():Bool; 82 | public function isYum(food:ObjectHelper):Bool; 83 | public function isObjIdYum(id:Int):Bool; 84 | public function isMeh(food:ObjectHelper):Bool; 85 | public function isSuperMeh(food:ObjectHelper):Bool; 86 | public function isObjSuperMeh(foodObjData:ObjectData):Bool; 87 | public function canEat(food:ObjectHelper):Bool; 88 | public function canEatObj(objData:ObjectData):Bool; 89 | 90 | public function canFeedToMe(food:ObjectHelper):Bool; 91 | public function canFeedToMeObj(objData:ObjectData):Bool; 92 | public function getMaxChildFeeding():Float; // gives back how much a child can be fed 93 | 94 | public function getFollowPlayer():PlayerInterface; 95 | public function getHeldPlayer():PlayerInterface; 96 | public function getHeldByPlayer():PlayerInterface; 97 | 98 | public function getCraving():Int; 99 | public function getCountEaten(foodId:Int):Float; 100 | public function getColor():Int; 101 | 102 | public function isSuperHot():Bool; 103 | public function isSuperCold():Bool; 104 | public function hasYellowFever():Bool; 105 | public function getClothingById(clothingId:Int):ObjectHelper; 106 | public function countLittleKids():Int; 107 | 108 | public var coldPlace(default, default):ObjectHelper; 109 | public var warmPlace(default, default):ObjectHelper; 110 | public var firePlace(default, default):ObjectHelper; 111 | public var lastTemperature(default, default):Float; 112 | 113 | public function getClosestPlayer(maxDistance:Int, onlyHuman:Bool = false):PlayerInterface; 114 | } 115 | -------------------------------------------------------------------------------- /openlife/auto/ObjectCode.hx: -------------------------------------------------------------------------------- 1 | package openlife.auto; 2 | 3 | @:expose 4 | class ObjectCode { 5 | private static var spring:Array = [ 6 | 3030, 3031, 3032, 3033, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3044, 1096, 662, 663, 664, 665, 1861, 2388, 2389, 2390 7 | ]; 8 | private static var rift:Array = []; 9 | 10 | public static function look(id:Int) {} 11 | 12 | public static function id(name:String):Array { 13 | // lower case and no spaces 14 | return switch (StringTools.replace(name.toLowerCase(), " ", "")) { 15 | case "milkweed": 16 | [50, 51, 52]; 17 | case "bighardrock": 18 | [32]; 19 | case "stone": 20 | [33]; 21 | case "sharpstone": 22 | [34]; 23 | // berry bush 24 | case "berrybush": 25 | [30]; 26 | case "languishingberrybush": // soil 27 | [392]; 28 | case "dryberrybush": // water 29 | [393]; 30 | case "berry" | "berries": 31 | [31]; 32 | case "emptyberrybush": 33 | [1135]; 34 | case "tulereeds" | "rulereed" | "reed" | "reeds": 35 | [121]; 36 | case "tule" | "tules": 37 | [123, 124]; 38 | case "basket": 39 | [292]; 40 | case "food": 41 | [ 42 | 30, // Wild Gooseberry Bush 43 | 31, // Gooseberry 44 | 40, // Wild Carrot 45 | 197, // Cooked Rabbit 46 | 253, // Bowl of Gooseberries 47 | 272, // Cooked Berry Pie 48 | 273, // Cooked Carrot Pie 49 | 274, // Cooked Rabbit Pie 50 | 275, // Cooked Berry Carrot Pie 51 | 276, // Cooked Berry Rabbit Pie 52 | 277, // Cooked Rabbit Carrot Pie 53 | 278, // Cooked Berry Carrot Rabbit Pie 54 | 402, // Carrot 55 | 518, // Cooked Goose 56 | 570, // Cooked Mutton 57 | 763, // Fruiting Barrel Cactus 58 | 768, // Cactus Fruit 59 | 805, // Wild Onion 60 | ]; 61 | case "trees" | "tree": 62 | [ 63 | 760, // Dead Tree 64 | 527, // Willow Tree 65 | 49, // Juniper Tree 66 | 67 | 2452, // Yule Tree 68 | 2454, // Spent Yule Tree 69 | 2455, // Yule Tree with Half Candles 70 | 71 | 406, // Yew Tree 72 | 153, // Yew Tree #branch 73 | 74 | 530, // Bald Cypress Tree 75 | 76 | 2142, // Banana Tree 77 | 2145, // Empty Banana Tree 78 | 79 | 48, // Maple Tree 80 | 63, // Maple Tree #branch 81 | 82 | 2135, // Rubber Tree 83 | 2136, // Slashed Rubber Tree 84 | 85 | 45, // Lombardy Poplar Tree 86 | 65, // Lombardy Poplar Tree 87 | 88 | 99, // White Pine Tree 89 | 100, // White Pine Tree with Needles 90 | 2450, // Pine Tree with Candles 91 | 2461, // Pine Tree with Cardinals 92 | 2434, // Pine Tree with One Gardland 93 | 2436, // Pine Tree with Two Gardland 94 | 95 | 1874, // Wild Mango Tree 96 | 1875, // Fruiting Domestic Mango Tree 97 | 1876, // Languishing Domestic Mango Tree 98 | 1922, // Dry Fertile Domestic Mango Tree 99 | 1923, // Wet Fertile Domestic Mango Tree 100 | ]; 101 | case "watersource": 102 | [ 103 | 511, // Pound 104 | 662, // Shallow Well 105 | 660, // Full Bucket of Water 106 | 1099, // Partial Buket Of Water 107 | ]; 108 | case "watersourcebucket": 109 | [663, // Deep Well 110 | 1097, // Full Deep Well 111 | 706, // Ice Hole 112 | ]; 113 | case "soilsource": 114 | [1138, // fertile soil 115 | 1101, // fertile soil pile 116 | ]; 117 | case "soilsourcebasket": 118 | [211, // Fertile Soil deposit 119 | 624, // Composted Soil 120 | ]; 121 | case "bucket": 122 | [659]; // Empty Bucket 123 | case "danger": 124 | // thanks Hetuw (Whatever) 125 | [ 126 | 2156, // Mosquito swarm 127 | 128 | 764, // Rattle Snake 129 | 1385, // Attacking Rattle Snake 130 | 131 | 1328, // Wild board with Piglet 132 | 1333, // Attacking Wild Boar 133 | 1334, // Attacking Wild Boar with Piglet 134 | 1339, // Domestic Boar 135 | 1341, // Domestic Boar with Piglet 136 | 1347, // Attacking Boar# domestic 137 | 1348, // Attacking Boar with Piglet# domestic 138 | 139 | 418, // Wolf 140 | 1630, // Semi-tame Wolf 141 | 420, // Shot Wolf 142 | 428, // Attacking Shot Wolf 143 | 429, // Dying Semi-tame Wolf 144 | 1761, // Dying Semi-tame Wolf 145 | 1640, // Semi-tame Wolf# just fed 146 | 1642, // Semi-tame Wolf# pregnant 147 | 1636, // Semi-tame Wolf with Puppy#1 148 | 1635, // Semi-tame Wolf with Puppy#2 149 | 1631, // Semi-tame Wolf with Puppy#3 150 | 1748, // Old Semi-tame Wolf 151 | // 1641, //@ Deadly Wolf 152 | 153 | 628, // Grizzly Bear 154 | 655, // Shot Grizzly Bear#2 attacking 155 | 653, // Dying Shot Grizzly Bear #3 156 | 631, // Hungry Grizzly Bear 157 | 646, // @ Unshot Grizzly Bear 158 | 635, // Shot Grizzly Bear#2 159 | 645, // Shot Grizzly Bear 160 | 632, // Shot Grizzle Bear#1 161 | 637, // Shot Grizzle Bear#3 162 | 654, // Shot Grizzle Bear#1 attacking 163 | 164 | 1789, // Abused Pit Bull 165 | 1747, // Mean Pit Bull 166 | 1712, // Attacking Pit Bull 167 | ]; 168 | default: 169 | [-1]; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /openlife/data/map/MapData.hx: -------------------------------------------------------------------------------- 1 | package openlife.data.map; 2 | 3 | import haxe.Timer; 4 | import haxe.ds.Vector; 5 | import openlife.data.*; 6 | import openlife.data.object.ObjectData; 7 | import openlife.data.object.player.PlayerInstance; 8 | 9 | @:expose 10 | class MapData { 11 | /** 12 | * Biome 2D array, id of ground 13 | */ 14 | public var biome:ArrayDataInt; 15 | 16 | /** 17 | * Floor 2D array, id of floor 18 | */ 19 | public var floor:ArrayDataInt; 20 | 21 | /** 22 | * Object 2D array, container format for object 23 | */ 24 | public var object:ArrayData>; 25 | 26 | // all chunks combined 27 | public var x:Int; 28 | public var y:Int; 29 | // max pos 30 | public var mx:Int; 31 | public var my:Int; 32 | 33 | public static final RAD:Int = 32; // 16 34 | 35 | public function new() { 36 | clear(); 37 | } 38 | 39 | public function clear() { 40 | mx = my = -999999999; 41 | y = x = 999999999; 42 | biome = new ArrayDataInt(); 43 | floor = new ArrayDataInt(); 44 | object = new ArrayData>(); 45 | } 46 | 47 | /** 48 | * Set map chunk 49 | * @param x Tile X 50 | * @param y Tile Y 51 | * @param width Tile Width 52 | * @param height Tile Height 53 | * @param string Data string buffer 54 | */ 55 | public function setRect(chunk:MapInstance, string:String) { 56 | // combine 57 | if (this.x > chunk.x) this.x = chunk.x; 58 | if (this.y > chunk.y) this.y = chunk.y; 59 | if (this.mx < chunk.x + chunk.width) this.mx = chunk.x + chunk.width; 60 | if (this.my < chunk.y + chunk.height) this.my = chunk.y + chunk.height; 61 | // create array 62 | var a:Array = string.split(" "); 63 | // data array for object 64 | var data:Array; 65 | var objectArray:Array = []; 66 | var array:Array> = []; 67 | // bottom left 68 | for (j in chunk.y...chunk.y + chunk.height) { 69 | for (i in chunk.x...chunk.x + chunk.width) { 70 | string = a.shift(); 71 | data = string.split(":"); 72 | biome.set(i, j, Std.parseInt(data[0])); 73 | floor.set(i, j, Std.parseInt(data[1])); 74 | // setup containers 75 | object.set(i, j, id(data[2])); 76 | } 77 | } 78 | } 79 | 80 | public function collisionChunk(player:PlayerInstance):Vector { 81 | // 16 + 1 82 | // 16 + 1 83 | var vector = new Vector((RAD * 2) * (RAD * 2)); 84 | var int:Int = 0; 85 | for (y in player.y - RAD...player.y + RAD) { 86 | for (x in player.x - RAD...player.x + RAD) { 87 | vector[int++] = false; 88 | var array = object.get(x, y); 89 | if (array == null) continue; 90 | var id = array[0]; 91 | if (id <= 0) continue; 92 | var data = new ObjectData(id); 93 | if (data == null) continue; 94 | vector[int - 1] = data.blocksWalking; 95 | } 96 | } 97 | return vector; 98 | } 99 | 100 | public function mapFile(file:sys.io.FileInput, inOffsetX:Int = 0, inOffsetY:Int = 0, inTimeLimitSec:Float = 0) { 101 | var startTime = Timer.stamp(); 102 | var line:Array = []; 103 | var x:Int = 0; 104 | var y:Int = 0; 105 | // break out when read fails 106 | // or if time limit passed 107 | while (inTimeLimitSec == 0 || Timer.stamp() < startTime + inTimeLimitSec) { 108 | try { 109 | line = file.readLine().split(" "); 110 | } catch (e:Dynamic) { 111 | trace("No more lines"); 112 | return; 113 | } 114 | // loading into 115 | x = Std.parseInt(line[0]); 116 | y = Std.parseInt(line[1]); 117 | biome.set(x, y, Std.parseInt(line[2])); 118 | floor.set(x, y, Std.parseInt(line[3])); 119 | object.set(x, y, id(line[4])); 120 | } 121 | } 122 | 123 | public function toString() { 124 | return 'x: $x y: $y maxX: $mx maxY: $my'; 125 | } 126 | 127 | /** 128 | * Generate Array container format from string buffer 129 | * @param string buffer data 130 | * @return Array Container format array 131 | */ 132 | public static function id(string:String, first:String = ",", second:String = ":"):Array { 133 | // postive is container, negative is subcontainer that goes into postive container 134 | // 0 is first container, untill another postive number comes around 135 | if (string == null || string.length == 0) return []; 136 | var a = string.split(first); 137 | var s:Array = []; 138 | var array:Array = []; 139 | for (i in 0...a.length) { 140 | // container split data 141 | s = a[i].split(second); 142 | // sub 143 | array.push(Std.parseInt(s[0])); 144 | for (k in 1...s.length - 1) { 145 | // subobjects 146 | array.push(Std.parseInt(s[k]) * -1); 147 | } 148 | } 149 | return array; 150 | } 151 | 152 | public static function stringID(a:Array):String { 153 | var string:String = ""; 154 | for (i in 0...a.length) { 155 | if (a[i] >= 0) { 156 | string += "," + a[i]; 157 | } else { 158 | string += ":" + (a[i] * -1); 159 | } 160 | } 161 | string = string.substr(1); 162 | return string; 163 | } 164 | 165 | public static function numSlots(a:Array):Int { 166 | var count:Int = 0; 167 | for (i in 1...a.length) { 168 | if (i >= 0) count++; 169 | } 170 | return count; 171 | } 172 | 173 | public static function toContainer(a:Array):Array { 174 | for (i in 1...a.length) { 175 | if (a[i] > 0) a[i] *= -1; 176 | } 177 | return a; 178 | } 179 | 180 | public static function toHeldObject(a:Array):Array { 181 | for (i in 1...a.length) { 182 | if (a[i] < 0) a[i] *= -1; 183 | } 184 | return a; 185 | } 186 | 187 | public static function getObjectFromContainer(a:Array, index:Int = 0):Array { 188 | var b:Array = [a[index + 1]]; 189 | a.remove(a[index + 1]); 190 | for (i in index + 2...a.length) { 191 | if (a[i] >= 0) break; 192 | b.push(a[i] * -1); 193 | a.remove(a[i]); 194 | } 195 | return b; 196 | } 197 | } 198 | 199 | class MapCollision implements openlife.auto.Pathfinder.MapHeader { 200 | public var rows(default, null):Int; 201 | public var cols(default, null):Int; 202 | public var data:Vector; 203 | public var radius = MapData.RAD; 204 | 205 | public function new(data:Vector) { 206 | this.data = data; 207 | cols = MapData.RAD * 2 + 1 * 0; 208 | rows = MapData.RAD * 2 + 1 * 0; 209 | } 210 | 211 | public function isWalkable(p_x:Int, p_y:Int):Bool { 212 | return !data[p_x + p_y * (MapData.RAD * 2)]; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /openlife/server/Biome.hx: -------------------------------------------------------------------------------- 1 | package openlife.server; 2 | 3 | import openlife.data.object.ObjectData.PersonColor; 4 | import openlife.data.object.ObjectHelper; 5 | import openlife.settings.ServerSettings; 6 | 7 | @:enum abstract BiomeTag(Int) from Int to Int { 8 | public var GREEN = 0; 9 | public var SWAMP = 1; 10 | public var YELLOW = 2; 11 | public var GREY = 3; 12 | public var SNOW = 4; 13 | public var DESERT = 5; 14 | public var JUNGLE = 6; 15 | public var BORDERJUNGLE = 15; // 8 or 15 16 | public var SNOWINGREY = 21; // 7 // its snow on top of mountains which should not be walkable 17 | public var OCEAN = 9; // deep ocean 18 | public var PASSABLERIVER = 13; 19 | public var RIVER = 17; // TODO deep river which is not walkable 20 | } 21 | 22 | @:enum abstract BiomeMapColor(String) from String to String { 23 | public var CGREEN = "FFB5E61D"; 24 | public var CSWAMP = "FF008080"; 25 | public var CYELLOW = "FFFECC36"; // savannah 26 | public var CGREY = "FF808080"; // badlands // bevor it was: FF404040 27 | public var CSNOW = "FFFFFFFF"; 28 | public var CDESERT = "FFDBAC4D"; 29 | public var CJUNGLE = "FF007F0E"; 30 | public var CBORDERJUNGLE = "FF007F00"; 31 | public var CSAND = "FFefe4b0"; 32 | public var CSNOWINGREY = "FF404040"; // its snow on top of mountains which should not be walkable 33 | public var COCEAN = "FF004080"; // deep ocean 34 | public var CRIVER = "FF0080FF"; // shallow water 35 | public var CPASSABLERIVER = "FF00E8FF"; // TODO use also for passable ocean? otherwise use biomeID: 22??? 36 | } 37 | 38 | @:enum abstract BiomeSpeed(Float) from Float to Float { 39 | // var truncMovementSpeedDiff = 0.1; 40 | // considered as bad biome for horses if speed < 0.999 41 | // TODO make fast for specialists 42 | public var SGREEN = 1; 43 | public var SSWAMP = 0.9; // 0.6 44 | public var SYELLOW = 1; 45 | public var SGREY = 0.98; 46 | public var SSNOW = 0.98; 47 | public var SDESERT = 0.98; 48 | public var SJUNGLE = 0.98; 49 | public var SCBORDERJUNGLE = 0.98; 50 | public var SSNOWINGREY = 0.01; 51 | public var SOCEAN = 0.01; 52 | public var SRIVER = 0.01; 53 | public var SPASSABLERIVER = 0.98; // 0.8; 54 | } 55 | 56 | // Heat is the player's warmth between 0 and 1, where 0 is coldest, 1 is hottest, and 0.5 is ideal. 57 | @:enum abstract BiomeTemperature(Float) from Float to Float { 58 | public var TGREEN = 0.45; 59 | public var TSWAMP = 0.2; 60 | public var TYELLOW = 0.4; 61 | public var TGREY = 0.3; 62 | public var TSNOW = 0; 63 | public var TDESERT = 1; // loved by black but still little bit too hot 64 | public var TJUNGLE = 0.7; // loved by brown 65 | public var TCBORDERJUNGLE = 0.6; // perfect for brown 66 | public var TSNOWINGREY = 0.0; 67 | public var TOCEAN = 0.2; 68 | public var TRIVER = 0.2; 69 | public var TPASSABLERIVER = 0.1; 70 | } 71 | 72 | class Biome { 73 | // Heat is the player's warmth between 0 and 1, where 0 is coldest, 1 is hottest, and 0.5 is ideal. 74 | public static function getBiomeTemperature(biomeTag:BiomeTag):Float { 75 | return switch biomeTag { 76 | case GREEN: TGREEN; 77 | case SWAMP: TSWAMP; 78 | case YELLOW: TYELLOW; 79 | case GREY: TGREY; 80 | case SNOW: TSNOW; 81 | case DESERT: TDESERT; 82 | case JUNGLE: TJUNGLE; 83 | case BORDERJUNGLE: TCBORDERJUNGLE; 84 | case SNOWINGREY: TSNOWINGREY; 85 | case OCEAN: TOCEAN; 86 | case RIVER: TRIVER; 87 | case PASSABLERIVER: TPASSABLERIVER; 88 | default: 0.5; 89 | } 90 | } 91 | 92 | public static function getBiomeDecayFactor(biomeTag:BiomeTag):Float { 93 | return switch biomeTag { 94 | // case GREEN: TGREEN; 95 | case SWAMP: ServerSettings.DecayFactorInSwamp; 96 | // case YELLOW: TYELLOW; 97 | // case GREY: TGREY; 98 | // case SNOW: TSNOW; 99 | // case DESERT: TDESERT; 100 | case JUNGLE: ServerSettings.DecayFactorInJungle; 101 | case BORDERJUNGLE: ServerSettings.DecayFactorInJungle; 102 | case SNOWINGREY: ServerSettings.DecayFactorInMountain; 103 | case OCEAN: ServerSettings.DecayFactorInDeepWater; 104 | case RIVER: ServerSettings.DecayFactorInDeepWater; 105 | case PASSABLERIVER: ServerSettings.DecayFactorInWalkableWater; 106 | default: 1; 107 | } 108 | } 109 | 110 | public static function IsBiomeLovedbyColor(biome:BiomeTag, player:GlobalPlayerInstance) { 111 | var lovedBiome = GetLovedBiomeByPlayer(player); 112 | return lovedBiome == biome; 113 | } 114 | 115 | public static function GetLovedBiomeByPlayer(player:GlobalPlayerInstance):BiomeTag { 116 | var personColor = player.getColor(); 117 | 118 | if (personColor == PersonColor.Ginger) return BiomeTag.SNOW; 119 | if (personColor == PersonColor.White) return BiomeTag.GREY; 120 | if (personColor == PersonColor.Brown) return BiomeTag.JUNGLE; 121 | if (personColor == PersonColor.Black) return BiomeTag.DESERT; 122 | 123 | return -1; 124 | } 125 | 126 | // TODO better set in ObjdData, since there could be more then one 127 | // TODO not used yet. Meant for getting biome experience if eaten 128 | 129 | /*public static function getBiomeByFood(food:ObjectHelper) : BiomeTag 130 | { 131 | return switch food.parentId { 132 | case 768: DESERT; // Cactus Fruit 133 | case 2143: JUNGLE; // banana 134 | case 4252: GREY; // Wild Garlic 135 | case 40: SNOW; // Wild Carrot 136 | default: -1; 137 | } 138 | }*/ 139 | public static function getLovedFoodIds(biomeTag:BiomeTag):Array { 140 | return switch biomeTag { 141 | case DESERT: [768, 197]; // Cactus Fruit // Cooked Rabbit 142 | case JUNGLE: [2143, 1880]; // banana // Mango Slices 143 | case GREY: [4252, 1242]; // Wild Garlic // Bowl of Sauerkraut 144 | case SNOW: [40, 2106]; // Wild Carrot // Cooked Fish 145 | default: []; 146 | } 147 | } 148 | 149 | public static function getBiomeAnimals(biomeTag:BiomeTag):Array { 150 | return switch biomeTag { 151 | case DESERT: [764]; // Rattle Snake 152 | case JUNGLE: [2156]; // Mosquito Swarm 153 | case GREY: [418]; // Wolf 154 | case SNOW: [1323]; // Wild Boar 155 | default: []; 156 | } 157 | } 158 | 159 | public static function getLovedPlants(biomeTag:BiomeTag):Array { 160 | return switch biomeTag { 161 | case DESERT: [763]; // Fruiting Barrel Cactus 162 | case JUNGLE: [2142]; // Banana Plant 163 | case GREY: [4251]; // Wild Garlic (on ground) 164 | case SNOW: [39]; // Dug Wild Carrot 165 | default: []; 166 | } 167 | } 168 | 169 | public static function IsWater(biome:BiomeTag):Bool { 170 | return biome == OCEAN || biome == PASSABLERIVER || biome == RIVER; 171 | } 172 | 173 | public function new() {} 174 | } 175 | -------------------------------------------------------------------------------- /badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | made-with-crayons 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /openlife/client/Fps.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import h2d.Bitmap; 4 | import h2d.Flow; 5 | import h2d.Object; 6 | import h2d.RenderContext; 7 | import h2d.Text; 8 | import h2d.Tile; 9 | import hxd.Timer; 10 | 11 | /** 12 | Primitive FPS counter. 13 | Adapted from openfl.display.FPS 14 | **/ 15 | class Fps extends Object { 16 | public var currentFPS(default, null):Int; 17 | public var averageFPS(default, null):Int; 18 | public var minFPS(default, null):Int; 19 | public var maxFPS(default, null):Int; 20 | 21 | var graph:Bitmap; 22 | var graphShader:GraphShader; 23 | 24 | var stats:Text; 25 | var avgStat:Text; 26 | var minStat:Text; 27 | var maxStat:Text; 28 | 29 | var resolution:Int; 30 | var packedResolution:Int; 31 | var frames:haxe.ds.Vector; 32 | 33 | public var goodThreshold:Int = 60; 34 | public var cautionThreshold:Int = 30; 35 | 36 | public function new(resolution:Int = 150, ?parent:Object) { 37 | super(parent); 38 | currentFPS = 0; 39 | 40 | var fnt = hxd.res.DefaultFont.get(); 41 | var f = new h2d.Flow(this); 42 | f.maxWidth = resolution; 43 | f.minWidth = resolution; 44 | f.horizontalSpacing = 4; 45 | // f.debug = true; 46 | avgStat = new Text(fnt, f); 47 | avgStat.textAlign = MultilineRight; 48 | minStat = new Text(fnt, f); 49 | minStat.textAlign = MultilineRight; 50 | maxStat = new Text(fnt, f); 51 | maxStat.textAlign = MultilineRight; 52 | stats = new Text(fnt, this); 53 | stats.textAlign = Right; 54 | stats.x = resolution; 55 | 56 | if (resolution > 512) resolution = 512; else if (resolution < 10) resolution = 10; 57 | this.resolution = resolution; 58 | 59 | graph = new Bitmap(Tile.fromColor(0xffffff, resolution, 60), this); 60 | graph.y = fnt.lineHeight * 2; 61 | graphShader = new GraphShader(); 62 | graphShader.graphValuesCount = resolution; 63 | graphShader.height = 60; 64 | graph.addShader(graphShader); 65 | packedResolution = Math.ceil(resolution / 4); 66 | graphShader.GraphValuesSize = packedResolution; 67 | for (i in 0...packedResolution) 68 | graphShader.graphValues[i] = new h3d.Vector(0, 0, 0, 0); 69 | 70 | frames = new haxe.ds.Vector(resolution); 71 | // for (i in 0...resolution) frames[i] = goodThreshold; 72 | } 73 | 74 | public function alignSelf(hor:FlowAlign = Right, ver:FlowAlign = Top) { 75 | switch (hor) { 76 | // case Left: x = 5; 77 | case Right: 78 | x = getScene().width - resolution - 5; 79 | case Middle: 80 | x = Math.round((getScene().width - resolution) / 2); 81 | default: 82 | x = 5; 83 | } 84 | switch (ver) { 85 | // case Top: y = 5; 86 | case Bottom: 87 | y = getScene().height - graph.y - graph.height - 5; 88 | case Middle: 89 | y = Math.round((getScene().height - graph.y - graph.height) / 2); 90 | default: 91 | y = 5; 92 | } 93 | } 94 | 95 | override private function sync(ctx:RenderContext) { 96 | super.sync(ctx); 97 | var ts = Std.int(1 / Timer.elapsedTime); 98 | 99 | var max = 0; 100 | var min = ts; 101 | 102 | var arr = frames; 103 | var sum = 0; 104 | for (i in 0...resolution - 1) { 105 | var v = arr[i + 1]; 106 | arr[i] = v; 107 | sum += v; 108 | if (max < v) max = v; 109 | if (min > v) min = v; 110 | } 111 | arr[resolution - 1] = ts; 112 | sum += ts; 113 | if (max < ts) max = ts; 114 | 115 | // max = this.max = this.max < 1 || this.max < max ? max : this.max - 1; 116 | var oi = 0; 117 | for (i in 0...packedResolution) { 118 | graphShader.graphValues[i].set(arr[oi++] / max, arr[oi++] / max, arr[oi++] / max, arr[oi++] / max); 119 | } 120 | graphShader.average = (sum / resolution) / max; 121 | graphShader.goodThreshold = goodThreshold / max; 122 | graphShader.cautionThreshold = cautionThreshold / max; 123 | // monitor avg 124 | 125 | currentFPS = ts; 126 | averageFPS = Std.int(sum / resolution); 127 | minFPS = min; 128 | maxFPS = max; 129 | 130 | // TODO: Less expensive stat display. 131 | // avgStat.text = "FPS\n" + averageFPS; 132 | // minStat.text = "MIN\n" + min; 133 | // maxStat.text = "MAX\n" + max; 134 | #if hl 135 | maxStat.text = "MEM MB\n" + Std.string(Std.int(hl.Gc.stats().currentMemory / Math.pow(1000, 2))); 136 | #end 137 | stats.text = averageFPS + " FPS\n" + Math.round(Math.round(Timer.elapsedTime * 10000) / 10) + "ms"; 138 | 139 | // Lag-gen 140 | // var j = 0; 141 | // var i = 0; 142 | // while (i < Math.random() * 500000) { 143 | // j = j + 1 & 8; i++; 144 | // } 145 | 146 | #if hl 147 | // var stats = hl.Gc.stats(); 148 | // TODO: Memory stats 149 | #end 150 | } 151 | } 152 | 153 | // Port of shader from Graphy 154 | // https://github.com/Tayx94/graphy/blob/master/Assets/Tayx/Graphy%20-%20Ultimate%20Stats%20Monitor/Shaders/GraphStandard.shader 155 | class GraphShader extends hxsl.Shader { 156 | static var SRC = { 157 | var pixelColor:Vec4; 158 | @var var calculatedUV:Vec2; 159 | @param var goodColor:Vec4 = vec4(0.462745, 0.831372, 0.227450, 1); 160 | @param var cautionColor:Vec4 = vec4(0.952941, 0.909803, 0, 1); 161 | @param var criticalColor:Vec4 = vec4(0.862745, 0.160784, 0.117647, 1); 162 | @param var goodThreshold:Float = 0.5; 163 | @param var cautionThreshold:Float = 0.25; 164 | @param var average:Float; 165 | // NOTE: The size of this array can break compatibility with some older GPUs 166 | // If you see a pink box or that the graphs are not working, try lowering this value 167 | // or using the GraphMobile.shader 168 | // Port note: It's a hacky implementation, because Heaps does not allow using `Array` and complains it should be 4-component-aligned. 169 | @param @const var GraphValuesSize:Int; // 512/4 170 | @param var graphValues:Array; 171 | @param var graphValuesCount:Float; 172 | // Graph height. Used to do more precise graph bars so they won't jump in thickness. 173 | @param var height:Float = 0; 174 | function fragment() { 175 | var xCoord = calculatedUV.x; 176 | var yCoord = 1 - calculatedUV.y; 177 | 178 | var pxSize = if (height != 0) 1 / height else 0.02; 179 | 180 | var color:Vec4; 181 | if (yCoord < average && yCoord > average - pxSize) { 182 | // Average white bar 183 | color = vec4(1, 1, 1, 1); 184 | } else if (yCoord < cautionThreshold && yCoord > cautionThreshold - pxSize) { 185 | // CautionColor bar 186 | color = cautionColor; 187 | } else if (yCoord < goodThreshold && yCoord > goodThreshold - pxSize) { 188 | // GoodColor bar 189 | color = goodColor; 190 | } else { 191 | var index = int(xCoord * graphValuesCount); 192 | var packed = graphValues[int(index / 4)]; 193 | index %= 4; 194 | var graphValue = if (index == 0) packed.r else if (index == 1) packed.g else if (index == 3) packed.b else packed.a; 195 | 196 | if (yCoord > graphValue) { 197 | // Set as transparent the part on top of the current point value 198 | discard; 199 | } else { 200 | // Assign the corresponding color 201 | if (graphValue > goodThreshold) { 202 | color = goodColor; 203 | } else if (graphValue > cautionThreshold) { 204 | color = cautionColor; 205 | } else { 206 | color = criticalColor; 207 | } 208 | // Point coloring 209 | if (graphValue - yCoord > (1. / (graphValuesCount - 1.)) * 4) { 210 | // color.a = yCoord * graphValue * .3; 211 | color.a = yCoord * .3 / graphValue; 212 | } 213 | } 214 | } 215 | 216 | // Fade the alpha of the sides of the graph 217 | if (xCoord < 0.03) { 218 | color.a *= 1 - (0.03 - xCoord) / 0.03; 219 | } else if (xCoord > 0.97) { 220 | color.a *= (1 - xCoord) / 0.03; 221 | } 222 | pixelColor = color; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /app/ClientAi.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openlife.auto.Role; 4 | import openlife.auto.Action; 5 | import openlife.resources.ObjectBake; 6 | import openlife.engine.EngineEvent; 7 | import openlife.engine.Utility; 8 | import openlife.auto.Automation; 9 | import openlife.engine.Program; 10 | import openlife.client.Client; 11 | import openlife.engine.EngineHeader; 12 | import openlife.engine.Engine; 13 | import openlife.data.object.player.PlayerInstance; 14 | import openlife.data.object.player.PlayerMove; 15 | import openlife.data.map.MapInstance; 16 | import openlife.data.map.MapChange; 17 | import openlife.data.object.ObjectData; 18 | import openlife.data.Target; 19 | import haxe.ds.Map; 20 | 21 | class Bot extends Engine implements EngineHeader { 22 | public var target:openlife.data.Target; 23 | public var auto:Automation; 24 | public var resetFlag:Bool = false; 25 | public var names = new Map(); 26 | 27 | var followingId:Int = -1; 28 | 29 | public var event:EngineEvent; 30 | 31 | private static var staticDelay:Float = 0; 32 | 33 | var reconnectBool:Bool = true; 34 | 35 | public function new(client:Client) { 36 | event = new EngineEvent(); 37 | super(this, event, client); 38 | client.onClose = close; 39 | client.onReject = function() { 40 | reconnectBool = false; 41 | } 42 | client.onAccept = function() { 43 | reconnectBool = true; 44 | } 45 | this.setPlayer = function() { 46 | // new player set 47 | auto = new Automation(program); 48 | #if script 49 | trace("EXECUTING SCRIPT"); 50 | Script.main(this); 51 | #end 52 | } 53 | } 54 | 55 | private function close() { 56 | // reconnect 57 | Sys.sleep(2); 58 | var relay = client.relayIn != null ? true : false; 59 | player = null; 60 | clear(); 61 | names.clear(); 62 | players.clear(); 63 | connect(reconnectBool, relay); 64 | } 65 | 66 | public function test() {} 67 | 68 | public function update() { 69 | client.update(); 70 | } 71 | 72 | // events 73 | public function playerUpdate(instances:Array) {} // PLAYER_UPDATE 74 | 75 | public function playerMoveStart(move:PlayerMove) { 76 | if (player.p_id == move.id) program.playerMainMove(player, move); 77 | } // PLAYER_MOVES_START 78 | 79 | public function playerOutOfRange(list:Array) {} // PLAYER_OUT_OF_RANGE 80 | 81 | public function playerName(id:Int, firstName:String, lastName:String) { 82 | names.set(id, firstName + " " + lastName); 83 | } // NAME 84 | 85 | public function apocalypse() {} // APOCALYPSE 86 | 87 | public function apocalypseDone() {} // APOCALYPSE_DONE 88 | 89 | public function posse(killer:Int, target:Int) {} // POSSE_JOIN 90 | 91 | public function following(follower:Int, leader:Int, color:Int) {} // FOLLOWING 92 | 93 | public function exiled(target:Int, id:Int) {} // EXILED 94 | 95 | public function cursed(id:Int, level:Int, word:String) {} // CURSED 96 | 97 | public function curseToken(count:Int) {} // CURSE_TOKEN_CHANGE 98 | 99 | public function curseScore(excess:Int) {} // CURSE_SCORE_CHANGE 100 | 101 | public function badBiomes(id:Int, name:String) {} // BAD_BIOMES 102 | 103 | public function vogUpdate() {} // VOG_UPDATE 104 | 105 | public function photo(x:Int, y:Int, signature:String) {} // PHOTO_SIGNATURE 106 | 107 | public function shutdown() {} // FORCED_SHUTDOWN 108 | 109 | public function global(text:String) {} // GLOBAL_MESSAGE 110 | 111 | public function war(a:Int, b:Int, status:String) {} // WAR_REPORT 112 | 113 | public function learnedTools(list:Array) {} // LEARNED_TOOL_REPORT 114 | 115 | public function toolExperts(list:Array) {} // TOOL_EXPERTS 116 | 117 | public function toolSlots(total:Int) {} // TOOL_SLOTS 118 | 119 | public function babyWiggle(list:Array) {} // BABY_WIGGLE 120 | 121 | public function saysLocation(x:Int, y:Int, text:String) {} // LOCATION_SAYS 122 | 123 | public function dying(id:Int, sick:Bool) { 124 | if (id != player.p_id) return; 125 | program.say("I AM DYING!"); 126 | } // DYING 127 | 128 | var found:Int = -1; 129 | 130 | public function says(id:Int, text:String, curse:Bool) { 131 | if (id == player.p_id) return; 132 | var words = text.split(" "); 133 | words.shift(); 134 | var index:Int = 0; 135 | if (words.indexOf("YOU") > -1 && (index = words.indexOf("KNOW") + 1) > 0) { 136 | found = -1; // auto.interp.stringObject(words.slice(index, words.length)); 137 | if (found == -1) { 138 | program.say("I DO NOT KNOW"); 139 | return; 140 | } 141 | program.say('I KNOW! ${new ObjectData(found).description}'); 142 | } 143 | if ((words.indexOf("FIND") > -1 || words.indexOf("GO") > -1) && found > -1) { 144 | var id = ObjectBake.dummies.get(found); 145 | id == null ? id = [found] : id.unshift(found); 146 | var pos = auto.select(id); 147 | if (pos == null) { 148 | program.say("I DID NOT FIND"); 149 | return; 150 | } 151 | program.say("I GO THERE NOW"); 152 | program.goto(pos.x, pos.y); 153 | } 154 | if ((index = words.indexOf("FOLLOW") + 1) > 0) { 155 | followingId = id; 156 | var p = players.get(followingId); 157 | program.goto(p.x, p.y); 158 | } 159 | if ((index = words.indexOf("HERE") + 1) > 0) { 160 | var p = players.get(id); 161 | program.goto(p.x, p.y); 162 | } 163 | if (words.indexOf("PICK") > -1 && words.indexOf("UP") > -1 || words.indexOf("USE") > -1) { 164 | program.use(player.x, player.y); 165 | } 166 | if (words.indexOf("SELF") > -1 || words.indexOf("EAT") > -1) { 167 | program.self(); 168 | } 169 | if (words.indexOf("DROP") > -1) { 170 | program.drop(player.x, player.y); 171 | } 172 | if ((index = words.indexOf("STOP") + 1) > 0) { 173 | followingId = -1; 174 | } 175 | if (words.indexOf("PING") > -1) { 176 | program.say("PONG"); 177 | } 178 | if (words.indexOf("MARCO") > -1) { 179 | program.say("POLO"); 180 | } 181 | } // PLAYER_SAYS 182 | 183 | public function emot(id:Int, index:Int, sec:Int) {} // PLAYER_EMOT 184 | 185 | public function mapChunk(instance:MapInstance) {} // MAP_CHUNK 186 | 187 | public function mapChange(change:MapChange) {} // MAP_CHANGE 188 | 189 | public function foodChange(store:Int, capacity:Int, ateId:Int, fillMax:Int, speed:Float, responsible:Int) { 190 | if (store / capacity < 0.2) program.say("F"); 191 | } // FOOD_CHANGE 192 | 193 | public function heatChange(heat:Float, foodTime:Float, indoorBonus:Float) {} // HEAT_CHANGE 194 | 195 | public function frame() {} // FRAME 196 | 197 | public function lineage(list:Array, eve:Int) {} // LINEAGE 198 | 199 | public function healed(id:Int) {} // HEALED 200 | 201 | public function monument(x:Int, y:Int, id:Int) {} // MONUMENT_CALL 202 | 203 | public function grave(x:Int, y:Int, id:Int) {} // GRAVE 204 | 205 | public function graveOld(x:Int, y:Int, pid:Int, poid:Int, age:Float, name:String, lineage:Array) {} // GRAVE_OLD 206 | 207 | public function graveMove(xs:Int, ys:Int, xd:Int, yd:Int, swapDest:Bool) {} // GRAVE_MOVE 208 | 209 | public function ownerList(x:Int, y:Int, list:Array) {} // OWNER_LIST 210 | 211 | public function valley(spacing:Int, offset:Int) {} // VALLEY_SPACING 212 | 213 | public function flight(id:Int, x:Int, y:Int) {} // FLIGHT_DEST 214 | 215 | public function homeland(x:Int, y:Int, name:String) {} // HOMELAND 216 | 217 | public function flip(x:Int, y:Int) {} // FLIP 218 | 219 | public function craving(id:Int, bonus:Int) {} // CRAVING 220 | 221 | // Used for actions and roles 222 | public function moveTo(x:Int, y:Int) { 223 | // if path not already calculated get one from the map 224 | // store the path in the bot 225 | // shift the path (remove the first element of the array) and store the node in a temp variable 226 | // check if the node is walkable - if not repath 227 | // if needed repath twice and give up 228 | // walk the node 229 | // wash and repeat every movement tick until at destination 230 | } 231 | } 232 | 233 | class Target {} 234 | --------------------------------------------------------------------------------