From 8ee70854eac351313dfe4e0090b5904c9661be6a Mon Sep 17 00:00:00 2001 From: djairoh Date: Fri, 6 Feb 2026 12:43:37 +0100 Subject: [PATCH] ft: predator mostly done --- .../predator/hammerheadRibozyme-hunting.ase | Bin 0 -> 3800 bytes .../predator/hammerheadRibozyme-hunting.png | Bin 0 -> 1667 bytes .../hammerheadRibozyme-hunting.png.import | 40 ++++++++++ .../predator/hammerheadRibozyme-hurt.ase | Bin 0 -> 2227 bytes .../predator/hammerheadRibozyme-hurt.png | Bin 0 -> 1937 bytes .../hammerheadRibozyme-hurt.png.import | 40 ++++++++++ .../molecular/molecular_player.gd | 28 +++++-- .../molecular/predator/hammerhead_predator.gd | 58 ++++++++++++-- .../predator/hammerhead_predator.tscn | 74 +++++++++++++++++- .../molecular/predator/state_hunting.gd | 26 ++++++ .../molecular/predator/state_hunting.gd.uid | 1 + .../molecular/predator/state_machine.gd | 10 +++ .../molecular/prey/state_fleeing.gd | 2 +- .../molecular/prey/state_idle.gd | 11 +-- .../molecular/prey/state_random_movement.gd | 5 +- evolve-die-repeat/shared/state_machine.gd | 6 ++ 16 files changed, 271 insertions(+), 30 deletions(-) create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.ase create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png.import create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.ase create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.png create mode 100644 evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.png.import create mode 100644 evolve-die-repeat/molecular/predator/state_hunting.gd create mode 100644 evolve-die-repeat/molecular/predator/state_hunting.gd.uid diff --git a/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.ase b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.ase new file mode 100644 index 0000000000000000000000000000000000000000..95adade2ea61ecc46540d230070f0ba4d1f55e0a GIT binary patch literal 3800 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 literal 0 HcmV?d00001 diff --git a/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png new file mode 100644 index 0000000000000000000000000000000000000000..48dfcdbf6a1525aaaf60d570369e9a880846e113 GIT binary patch literal 1667 zcmV-}27LL6P) zF>4!16vzKMVypv+D^vwz(ij}D3LWHx+f*S4u1Vo8l|R5W0)GTUzCw!N!ktTDOyD40 zFjuIM1BP_QxCkZ$E1ZICHihz5qw(%q+Ih1xqkTUxi5zF%{^z|nZ+2#O4G4lD2!bF8 zf>0(Zt}j3cpZ(5O1OR)zvzd76Zfr*Y@aFSJ<2XyQLG7E^kOUBn*zasb?dIg~r-R9# z>z6C&536X^MgV~RuxgY9Nj9#}>v8)gHXh}@C_&JnAicW8OmNz31fY^cq?JD+ z?+Eaa9d3KV;?Sr}!AbOoRqt&WRRHy3_Av=mz5D^@7pdEcM}d=-xNS~*!eXe?lXesL ze8S-mTQ+hhz+UgHLP6hd7E}JnmP%6K%joZhb`v;0su$vaE*rm@HqdTPvXV*^(H~ah zFOz`W>z!4oecgs`$7aV1*^qB^ADH?dn8tt(`P~GgDG*hGqTU>*0TC!Ej}=<$m&a5DLxiQkPYfSR4B1K?t9%(ncMWP_$#H2{^=mwdGodn%-&#e+gJqka2DAIl$*1R9qNYyRBv0i0Zew=sl7ZlY(X(V zzTxtV?tjkqSvpG3Of@(GvkOhjdQKa8pix>6)~l11dq2DA4z*eVl9GWE%%@#&TLWN{ z|2f+?o&wn@OL-`IIz)EVO&|ccRz~H#L8)p`kDCC$?5vH^`nA;TI`9FvF<|O06kkj2vQGb0TwbfG-&ra^RtK0~pxSm?`S&&Wx$$}WlN19Se#!DbNBeA3 zAlsurlJn`&I9I`+rn%_yUxop|*4Z!CdjJXuj;Hli8k;vbKB@zl20e9vYsX9Vn^L6z zxinktI6B%(7f;M{Bv}0%pMN>3dwm2zJ(vIT)lK{c15W*o%^Lvz1br4E_}^M5s^{AE zDM8vnv9u$`Y8zfY2bzHB4@19|j?5BI=t9yS1W9Vc_aG4c;pk`YL|sVM4Iv3h6F<-Y zHMv$Bi2ktjJKYIx)kdkAAIe6G38LSFrH{sO{wJ|Oyi*I!u2b~m=8|960jkE&q8C{eJ2%bRS^15#jB11?qFIH1AV?&)7k)g4Z1{pYSt6 z^!um3@QpC)gfy1b`Gn~AOTUwyVCsZ_u4f;^4WCPLfUVq`8oAkn`_qVr+bH8N_0MYN4{=zH3ybsSWqw@jL@0)(-zf|Gv zbp^YP2=8xi0RXQ%(b76!Mb-aobroN#LjdK}@6!KHJNfzgpV4k(Qu^pn)IqO;cZ0!F zJD(u^{b3c2&70Ed2i*DJ=}qvn)l~qqCjy^7S&CJ^2kCBX$Ie$Ni+;cl|9kav+%lAY z9UA=wH~x1XZ1gUjkluI0h58<$GU^A)?SDZK1VIo4K@bE%5Cowpegd3^2`ZBKNd*7^ N002ovPDHLkV1gc;Kz{%L literal 0 HcmV?d00001 diff --git a/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png.import b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png.import new file mode 100644 index 0000000..de3d1c9 --- /dev/null +++ b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hunting.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://30uwkdbnuu3h" +path="res://.godot/imported/hammerheadRibozyme-hunting.png-9b9929a81db6474a29fbff93ee4f1cef.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://molecular/assets/predator/hammerheadRibozyme-hunting.png" +dest_files=["res://.godot/imported/hammerheadRibozyme-hunting.png-9b9929a81db6474a29fbff93ee4f1cef.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.ase b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.ase new file mode 100644 index 0000000000000000000000000000000000000000..192c797b150f84c14087d77237f4db11a77db58e GIT binary patch literal 2227 zcmcJQdpOj49>;$jl0qUv!j#;OTZ2(9qinfv9NSsgJbQNA-T(JH&%B=R%=4S)`+mK@-|sUmk_hsq zN(?y#FBGx^L6CpI`|B|NI{m-Qz7a=|^+oZ2uWAMnR8ap~)#!iM6e5Tc>I6{BG5}~D zkPURpeFP+Qy#U^sp#xP+4S`x7MnL;?6JT8RX<*HBFW~6(E#O&w(f5gtw!r#$PvDaR z0`L{30LXpW0yI0M0}L`S0A{)91H0lyWR!}2^}gp4@Tj8b8Cx|pFz%=*Th*~+K$@pD za5}>VsIF!KJf-6fOf*jdR@meK``t-E=JhJz?mY^?eF_>tMKxU@%IG-I!udO(XKXw$ zjZ_G%p)>;rXhXocPalLNXh~eOccR*$9_R*&fmWar=mW}tCZGoB015zmPzP_224m0# zR}ckDPy|1a12fP9ClCS~Pyr8+00W2**&#TjhRBc?!a`Dr2^k?Eq=RUX>#!&mO6)(j z5b`3s5&OvVmoJB23=NU^20?bB5M(d13lYNnpFx%M5^<7O7rammRU8E34}d{=4gj z+S$vJY*m->j~A~jRjN4XF=JqlzmXMQ@NvZ?mhLW7yDe?wR=p~iXpAD$=*E6)M#)oz zAnInLmQAkVa}s4_ng5eAZBWUdZL*5);CWfC)ZvC%OYIZ$O?l00cVuQ5(_8mmHat-2 zba`VXwuT?Sn$2#$y!Xopd#q*D$J|XP(JimXgZ07^Q%137O4>N#CrtzqK4X0DXLe1u zL(Z-Ii)H~1j1OMxhQU}Wu0AJd4F6QxJZyYUoj)`X|9$0DP{mR+o!l$O>q#(Fjmu;& z?v_<{NqRKYX;(nI@L)2GpSHNp`F!1t@69a?us<&lY}uH^5L;z`WUstkyza(rd!pmC zt`d{U3|lrf{U~VTzBVlyV-@%H=UC&S(`4>)Z964KIO#o^o5Uz{r5r(Pufz-9V^hI4 z5|(%uGX)2CeQ^g8^85Y|!e`N`kU`}B>s08$shA_F7x zzRY=H!q&OTC+kaVu~Wv~<(Qnv;*_k2q(a%SUnA?Vi`1s4VK-za|AO6e4QPtHR@(g1iTGG4ReCzhdqy9>p+O@);Robo?w}SpG^)RT)|M(67whf-5cB*gV)!(Qy3z zx{Kytue<1vM|9n7oyF1n?6~qqJ4xx+d|x$mUU#^}RkZFqWVdjnL2*%9)4gj`FaLvU z^|$0ZbElPYd9&TlTK?quD@G(L#m`vw_`v8akC(X-9PSbP_tbiH;d)}qHhy7kaplXL zUNS5C6S_V12HLEnyvaCrkme*ot{kqHAOM!Hy0i@|-T3M%e!{GSA(LiI>;jR~HS3SO%aM5Hsv;D5>Ew%hO)GI=M z*<6?0Q3@-+KT@dIZVKDPVgLXD literal 0 HcmV?d00001 diff --git a/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.png b/evolve-die-repeat/molecular/assets/predator/hammerheadRibozyme-hurt.png new file mode 100644 index 0000000000000000000000000000000000000000..7bd82fd103a7ec9388a651b3d3ff6fd6d98e56b7 GIT binary patch literal 1937 zcmV;C2X6R@P) zzfU7a6vw~LUFGIFw`bwPCotzK?j6MqmeL?WgHDJx$ti>3#bvOrbDA8$ed5xO?{msz-_wcxd zm-|~3D|GXYoWJsg{0^&@g4zBVOJAfE|fcX$aA&Zd1 zBUe4#^*L6DW@XU<0Do3Cy$+=aAbR2W6dnLPj3&4l1{+F^=cNAm!26JA@i5&XEgzsj5Jv3@Sxmrps{h>Ml15zNO zSgFr>BM_X0RRk$Inp}rmPow5$e_hbbERT=s0vPc1)4^;ZES3$ZIfIe|kT}CcDX-d9 zkwYE*EJpx0`)7+N&YK~yxx*vOC`em(Uk2FRX#r4kvL93%v~hG@AcJ4-Z&9w60^;Wo zq*w=~Ta^EkgE{f*hjMcoBaYdhIO%^ckod)MSA}I8bS@H%1{}qP)*=3m+tD1!5Tihv z`9C2>eN`t@jsS!>OOuCki;@hbPEM;H$Li1wO>oZsS=OOUr@iILd-B|jPn({6KHxBe zEXIaYV{8!sm>pRk9=GNWusG{@7)?;D)Kja&k&N+a(<@f$coga&uK+w{^(#bihL={NI)p9Nz2!vX6y=sJ_t z7U1D=%b!(KK5JPQtc(DrA?Lu28t_+b%i}nu4%-3Yu4A2X5kQf+1)%CoO=ZU|FkLiI zBNGQmnitD=KTIP`{POxFsX$lRSuxoOOpj33MF7(lz*)n+2wZnka{xC`>y0fFm9hhv zlBO7WH5SQJ0P%Xc>F8{$YKp}Y0pgrMRhVMgQL~Dv;^)TXs2b)39QnUku53R?`D`gr z+L0jYj*U+7N6ma$hkw%-njEAdet>xmKu*B<%qHN!2CvukJAgz_!q+|X$Ay2@L5iFG zxa#19+Dl(tTwG32u9g7sKLwa-$I(_EiI-T^c=2gVF`7-GA{7lhH^5zAbO z7JsJuKjmr(7nc(N=7>HHy356BKlg-}S|>(bj|HMVIO^0>EW@N3FAnh{-?CS?<*xMD!5FCnVS9p>gOTL48E!94`84DsYC$T3AjAkmN&u4BH)T> zx)1)A#U}eRIR_AtJ41Y*yp4;u&v zMw7@gY!p8cx5GhpQ$IvuqxcEa?N5CXET%)f?qGT<9OdohYH9if*e!k_m=1_`MMXR6 zBk}5ot>OpFw?EStN&WM00iW7l;B&pi6Fwk#?O|`1|L9v6haXFbrJOI}Q5Jg_aPnc%EmS=*=3HHxl1Ha!sxXQKo2@=8f!Byb* z`{%E@7C&K<{h2v{yf@r``VV*CzD}KB```+9-@XPqB~PCLAfJu={`YIZ-u?5}Kz{&c zzwKdFOtoL@TOodI{u29h-f{Npw_mFRJU>36{m#FS_xa-+JU>1K{@?a3z(qViKH>An zHvoY4ySH=p+r9<72ovnr^2sXl!AR6WNFE&^>|pTZ*FQsZ-#j{iY4&Fx0hYZil}GUd z%U>4Eqxb>y?AMY4iB3T00(m<9T!ef&-udHaob?OS?cuzNpD@XOt(|;Wt9caq=u({N z559BKX>WOy`q8DZvwi`+;n3Ni5n30)AKnMW5BRS@Sr^Dw@kS)|$vR+y{YJeUH}9#> zBJ!U4EJBC void: var screen_size = get_viewport_rect().size @@ -20,6 +22,7 @@ func _ready() -> void: attack_timer.wait_time = attack_duration attack_cooldown_timer.wait_time = attack_cooldown_duration + func _process(delta): velocity = Vector2.ZERO if Input.is_action_pressed("move_right"): @@ -33,10 +36,15 @@ func _process(delta): if Input.is_action_pressed("try_attack"): try_attack() + if not velocity.is_zero_approx(): - self.rotation = atan2(velocity.y, velocity.x) + self.desired_rotation = atan2(velocity.y, velocity.x) + + # smoothly rotate + if self.rotation != self.desired_rotation: + self.rotation = lerp_angle(self.rotation, self.desired_rotation, clampf(4 * delta, 0, 1)) + move_and_collide(speed * velocity * delta) - #position += speed * velocity * delta position = GameManager.get_boundaried_position(position) func try_attack() -> void: @@ -60,7 +68,7 @@ func _on_cooldown_timeout() -> void: func _on_attack_hit(body: Node2D) -> void: var hit_hittable = false - if body.is_in_group("prey") or body.is_in_group("predators"): + if body.is_in_group("prey") or body.is_in_group("predator"): if body.has_method("handle_damage"): body.handle_damage(damage, self) hit_hittable = true @@ -70,3 +78,7 @@ func _on_attack_hit(body: Node2D) -> void: await get_tree().create_timer(0.2).timeout sprite.play("default") # TODO: resource handling logic + +func handle_damage(dmg: int, src: Node) -> void: + # TODO: damage logic + pass diff --git a/evolve-die-repeat/molecular/predator/hammerhead_predator.gd b/evolve-die-repeat/molecular/predator/hammerhead_predator.gd index 29822de..45742ae 100644 --- a/evolve-die-repeat/molecular/predator/hammerhead_predator.gd +++ b/evolve-die-repeat/molecular/predator/hammerhead_predator.gd @@ -1,14 +1,18 @@ extends AbstractPredator2D -# TODO: attacking logic + behaviour -# TODO: movement is buged (seems to not move/teleport somewhat +# FIXME: (general) tracking across wrapping boundary # TODO: mirroring (thought, extracct that to general function/resource? -@onready var sprite = $AnimatedSprite2D -@onready var fsm = $StateMachine - -@export var speed = 0.8 +var can_attack: bool = true var desired_rotation: float = self.rotation +@onready var sprite = $AnimatedSprite2D +@onready var fsm: StateMachine = $StateMachine +@onready var attack_cooldown_timer: Timer = $AttackCooldownTimer + +@export var damage: int = 15 +@export var attack_range = 20 +@export var sight_range = 200 +@export var speed = 0.8 func _ready() -> void: health = maxHealth @@ -24,7 +28,7 @@ func _process(delta: float) -> void: func _physics_process(delta: float) -> void: pass -# FIXME: (also goes for prey) this is framerate dependent +# FIXME: (also goes for prey) this is framerate dependent UNLESS called from a _physics function. func move(motion: Vector3, mod: float = 1.0) -> void: move_and_collide(Vector2(motion.x, motion.y).normalized() * self.speed * mod) # Moves along the given vector self.desired_rotation = atan2(motion.y, motion.x) @@ -32,7 +36,45 @@ func move(motion: Vector3, mod: float = 1.0) -> void: # Apply boundary to new position position = GameManager.get_boundaried_position(position) +func try_attack(target: Node) -> void: + if not can_attack: + return + attack(target) + +func attack(target: Node) -> void: + can_attack = false + var hit_hittable = false + if target.is_in_group("prey") or target.is_in_group("player"): + if target.has_method("handle_damage"): + target.handle_damage(damage, self) + hit_hittable = true + elif target.is_in_group("resources"): + # TODO: resource handling logic + pass + if hit_hittable: + attack_cooldown_timer.start() + + +func handle_damage(dmg: int, src: Node) -> void: + health = max(0, health-dmg) + if health == 0: + die() + if health < maxHealth/2: + become_injured() + fsm.transition_to_next_state(fsm.States.FLEEING, {"threat": src}) + elif health < maxHealth: + become_injured() + fsm.transition_to_next_state(fsm.States.HUNTING, {"target": src}) + +func die() -> void: + super.die() + +func become_injured() -> void: + sprite.play("Hurt") func _on_sight_body_entered(body: Node2D) -> void: - if body.is_in_group("prey") or (health < maxHealth and body.is_in_group("player")): + if fsm.map(fsm.state) != fsm.States.HUNTING and body.is_in_group("prey") or (health < maxHealth and body.is_in_group("player")): fsm.transition_to_next_state(fsm.States.HUNTING, {"target": body}) + +func _on_attack_cooldown_timer_timeout() -> void: + can_attack = true diff --git a/evolve-die-repeat/molecular/predator/hammerhead_predator.tscn b/evolve-die-repeat/molecular/predator/hammerhead_predator.tscn index fc2b904..cea4fa0 100644 --- a/evolve-die-repeat/molecular/predator/hammerhead_predator.tscn +++ b/evolve-die-repeat/molecular/predator/hammerhead_predator.tscn @@ -2,9 +2,12 @@ [ext_resource type="Script" uid="uid://d07cjelbqbiug" path="res://molecular/predator/hammerhead_predator.gd" id="1_xp037"] [ext_resource type="Texture2D" uid="uid://ch5rddsumyyhm" path="res://molecular/assets/predator/predator-healthy.png" id="2_34kwa"] +[ext_resource type="Texture2D" uid="uid://30uwkdbnuu3h" path="res://molecular/assets/predator/hammerheadRibozyme-hunting.png" id="3_0ts4d"] [ext_resource type="Script" uid="uid://cygrmt03sx0k1" path="res://molecular/predator/state_machine.gd" id="3_xp037"] [ext_resource type="Script" uid="uid://xbiqj7ubmj7d" path="res://molecular/prey/state_idle.gd" id="4_8a23j"] +[ext_resource type="Texture2D" uid="uid://jyuf4lgjo64" path="res://molecular/assets/predator/hammerheadRibozyme-hurt.png" id="4_shhro"] [ext_resource type="Script" uid="uid://ubcu8fdfxxj1" path="res://molecular/prey/state_random_movement.gd" id="5_6rsu5"] +[ext_resource type="Script" uid="uid://bc7apl71t0q04" path="res://molecular/predator/state_hunting.gd" id="8_7qt2q"] [sub_resource type="AtlasTexture" id="AtlasTexture_8a23j"] atlas = ExtResource("2_34kwa") @@ -18,6 +21,30 @@ region = Rect2(64, 0, 64, 64) atlas = ExtResource("2_34kwa") region = Rect2(128, 0, 64, 64) +[sub_resource type="AtlasTexture" id="AtlasTexture_nu6jw"] +atlas = ExtResource("3_0ts4d") +region = Rect2(0, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_8inuv"] +atlas = ExtResource("3_0ts4d") +region = Rect2(64, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_orf3n"] +atlas = ExtResource("3_0ts4d") +region = Rect2(128, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_vkkje"] +atlas = ExtResource("4_shhro") +region = Rect2(0, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jfmyn"] +atlas = ExtResource("4_shhro") +region = Rect2(64, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_f26lq"] +atlas = ExtResource("4_shhro") +region = Rect2(128, 0, 64, 64) + [sub_resource type="SpriteFrames" id="SpriteFrames_shhro"] animations = [{ "frames": [{ @@ -33,6 +60,34 @@ animations = [{ "loop": true, "name": &"Healthy", "speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_nu6jw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_8inuv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_orf3n") +}], +"loop": true, +"name": &"Hunting", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_vkkje") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jfmyn") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_f26lq") +}], +"loop": true, +"name": &"Hurt", +"speed": 5.0 }] [node name="HammerheadPredator" type="CharacterBody2D" unique_id=678504815 groups=["predator"]] @@ -45,18 +100,20 @@ maxHealth = 50 metadata/_custom_type_script = "uid://dgfimmq53whll" [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=410999609] +rotation = 4.712389 sprite_frames = SubResource("SpriteFrames_shhro") -animation = &"Healthy" +animation = &"Hunting" [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." unique_id=1596156928] light_mask = 4 visibility_layer = 4 position = Vector2(0.11167908, 1.1167793) +rotation = -1.5707964 polygon = PackedVector2Array(-22.184862, -27.994831, 23.481365, -27.21198, 13.82622, 25.891317, -6.005971, 25.891317) [node name="StateMachine" type="Node" parent="." unique_id=1857729810 node_paths=PackedStringArray("initial_state")] script = ExtResource("3_xp037") -initial_state = NodePath("Idle") +initial_state = NodePath("RandomMovement") metadata/_custom_type_script = "uid://ck7k8ht54snsy" [node name="Idle" type="Node" parent="StateMachine" unique_id=265876039] @@ -73,6 +130,10 @@ metadata/_custom_type_script = "uid://co2xp7gauamql" [node name="Timer" type="Timer" parent="StateMachine/RandomMovement" unique_id=447822526] one_shot = true +[node name="Hunting" type="Node" parent="StateMachine" unique_id=1569866955] +script = ExtResource("8_7qt2q") +metadata/_custom_type_script = "uid://co2xp7gauamql" + [node name="Sight" type="Area2D" parent="." unique_id=1608385873] collision_layer = 0 collision_mask = 7 @@ -80,6 +141,15 @@ collision_mask = 7 [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Sight" unique_id=1707240701] light_mask = 4 visibility_layer = 4 +position = Vector2(-1.0900421, 1.6350927) +rotation = -1.5707964 polygon = PackedVector2Array(-27.769547, -29.426758, 31.88504, -29.184647, 12.700996, 28.7294, 56.058624, 148.93633, 22.979004, 163.77974, -19.854843, 161.65926, -53.782654, 143.84715, -8.333115, 30.157196) +[node name="AttackCooldownTimer" type="Timer" parent="." unique_id=435253442] +wait_time = 0.5 +one_shot = true + +[connection signal="timeout" from="StateMachine/Idle/Timer" to="StateMachine/Idle" method="_on_timer_timeout"] +[connection signal="timeout" from="StateMachine/RandomMovement/Timer" to="StateMachine/RandomMovement" method="_on_timer_timeout"] [connection signal="body_entered" from="Sight" to="." method="_on_sight_body_entered"] +[connection signal="timeout" from="AttackCooldownTimer" to="." method="_on_attack_cooldown_timer_timeout"] diff --git a/evolve-die-repeat/molecular/predator/state_hunting.gd b/evolve-die-repeat/molecular/predator/state_hunting.gd new file mode 100644 index 0000000..05c79cb --- /dev/null +++ b/evolve-die-repeat/molecular/predator/state_hunting.gd @@ -0,0 +1,26 @@ +extends State + +var target: Node2D + +func enter(previous_state_path: String, data := {}) -> void: + if data.has("target"): + target = data["target"] + owner.sprite.play("Hunting") + else: + # default behaviour; do nothing + finished.emit(owner.fsm.States.IDLE, {}) + +func physics_update(_delta: float) -> void: + if target == owner or target == null or owner.position.distance_to(target.position) > owner.sight_range: + finished.emit(owner.fsm.States.IDLE, {}) + return + + # alternatively, we could use a collision shape and inbuilt signals, but im not sure if that works better (mixing signals and custom fsm stuff i mean + if owner.position.distance_to(target.position) > owner.attack_range: + owner.move(move_towards(target.position)) + else: + owner.attack(target) + +func move_towards(pos: Vector2) -> Vector3: + var diff = target.position - owner.position + return Vector3(diff.x, diff.y ,0) diff --git a/evolve-die-repeat/molecular/predator/state_hunting.gd.uid b/evolve-die-repeat/molecular/predator/state_hunting.gd.uid new file mode 100644 index 0000000..fe62562 --- /dev/null +++ b/evolve-die-repeat/molecular/predator/state_hunting.gd.uid @@ -0,0 +1 @@ +uid://bc7apl71t0q04 diff --git a/evolve-die-repeat/molecular/predator/state_machine.gd b/evolve-die-repeat/molecular/predator/state_machine.gd index a57990a..665b3e9 100644 --- a/evolve-die-repeat/molecular/predator/state_machine.gd +++ b/evolve-die-repeat/molecular/predator/state_machine.gd @@ -16,3 +16,13 @@ func transition_to_next_state(target: int, data: Dictionary = {}) -> void: States.FLEEING: _transition_to_next_state("Fleeing", data) States.HUNTING: _transition_to_next_state("Hunting", data) _: push_error("Trying to transition to unknown state {target}") + +func map(state: Node) -> States: + match state.name: + "Idle": return States.IDLE + "RandomMovement": return States.RANDOMMOVEMENT + "Feeding": return States.FEEDING + "Fleeing": return States.FLEEING + "Hunting": return States.HUNTING + _: push_error("Unknown state {state.name}") + return map(self.initial_state) diff --git a/evolve-die-repeat/molecular/prey/state_fleeing.gd b/evolve-die-repeat/molecular/prey/state_fleeing.gd index 0623491..659b39f 100644 --- a/evolve-die-repeat/molecular/prey/state_fleeing.gd +++ b/evolve-die-repeat/molecular/prey/state_fleeing.gd @@ -8,7 +8,7 @@ func enter(previous_state_path: String, data := {}) -> void: threat = data["threat"] else: # default behaviour; do nothing - threat = owner + finished.emit(owner.fsm.States.IDLE, {}) func physics_update(_delta: float) -> void: if owner.position.distance_to(threat.position) > threshold or threat == owner: diff --git a/evolve-die-repeat/molecular/prey/state_idle.gd b/evolve-die-repeat/molecular/prey/state_idle.gd index 0e9cc81..50bb7cc 100644 --- a/evolve-die-repeat/molecular/prey/state_idle.gd +++ b/evolve-die-repeat/molecular/prey/state_idle.gd @@ -2,25 +2,18 @@ extends State @onready var timer = $Timer var dir = Vector3(1, 1, 0) -var threshold = 1 -var max = 30 func enter(previous_state_path: String, data := {}) -> void: timer.start((float)(randi() % 5)/5) func physics_update(_delta: float) -> void: - if threshold == max: - owner.move(_delta * Vector3(randfn(0, 1), randfn(0, 1), 0), 4) - threshold = 1 - else: - threshold += 1 + owner.move(_delta * Vector3(randfn(0, 1), randfn(0, 1), 0), 1) func _on_timer_timeout() -> void: if (randi() % 4 != 0): finished.emit(owner.fsm.States.RANDOMMOVEMENT, {}) else: - finished.emit(owner.fsm.States.RANDOMMOVEMENT, {}) -# finished.emit(owner.fsm.States.IDLE, {}) + finished.emit(owner.fsm.States.IDLE, {}) func exit() -> void: timer.stop() diff --git a/evolve-die-repeat/molecular/prey/state_random_movement.gd b/evolve-die-repeat/molecular/prey/state_random_movement.gd index 3a48457..2cee56d 100644 --- a/evolve-die-repeat/molecular/prey/state_random_movement.gd +++ b/evolve-die-repeat/molecular/prey/state_random_movement.gd @@ -4,7 +4,7 @@ extends State var dir: Vector3 = Vector3(0,0,0); func enter(previous_state_path: String, data := {}) -> void: - timer.start((float)(randi() % 10)/20) + timer.start((float)(randi() % 10)/5) dir = calc_dir(randi() % 360) func physics_update(_delta: float) -> void: @@ -14,7 +14,8 @@ func calc_dir(angle: float) -> Vector3: return Vector3(cos(angle), sin(angle), 0) func _on_timer_timeout() -> void: - finished.emit(owner.fsm.States.IDLE, {}) + finished.emit(owner.fsm.States.RANDOMMOVEMENT, {}) + #finished.emit(owner.fsm.States.IDLE, {}) func exit() -> void: timer.stop() diff --git a/evolve-die-repeat/shared/state_machine.gd b/evolve-die-repeat/shared/state_machine.gd index 4af2e2b..0a1ec17 100644 --- a/evolve-die-repeat/shared/state_machine.gd +++ b/evolve-die-repeat/shared/state_machine.gd @@ -40,3 +40,9 @@ func _transition_to_next_state(target_path: String, data: Dictionary = {}) -> vo func transition_to_next_state(target: int, data: Dictionary = {}) -> void: push_error("Child FSM failed to implement transition function.") + +# maps child node name to States enum +# requires the recursive stap as push_error does not count as a valid return value. +func map(state: Node) -> int: + push_error("Child FSM failed to implement map function.") + return -1