grymlahv

pigman

Jul 1st, 2020
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.92 KB | None | 0 0
  1. local assets =
  2. {
  3. Asset("ANIM", "anim/ds_pig_basic.zip"),
  4. Asset("ANIM", "anim/ds_pig_actions.zip"),
  5. Asset("ANIM", "anim/ds_pig_attacks.zip"),
  6. Asset("ANIM", "anim/ds_pig_boat_jump.zip"),
  7. Asset("ANIM", "anim/pig_build.zip"),
  8. Asset("ANIM", "anim/pigspotted_build.zip"),
  9. Asset("ANIM", "anim/pig_guard_build.zip"),
  10. Asset("ANIM", "anim/werepig_build.zip"),
  11. Asset("ANIM", "anim/werepig_basic.zip"),
  12. Asset("ANIM", "anim/werepig_actions.zip"),
  13. Asset("ANIM", "anim/pig_token.zip"),
  14. Asset("SOUND", "sound/pig.fsb"),
  15. }
  16.  
  17. local PIG_TOKEN_PREFAB = "pig_token"
  18.  
  19. local prefabs =
  20. {
  21. "meat",
  22. "monstermeat",
  23. "poop",
  24. "tophat",
  25. "strawhat",
  26. "pigskin",
  27. PIG_TOKEN_PREFAB,
  28. }
  29.  
  30. local MAX_TARGET_SHARES = 5
  31. local SHARE_TARGET_DIST = 30
  32.  
  33. local function GetPigToken(inst)
  34. local token = next(inst.components.inventory:GetItemByName(PIG_TOKEN_PREFAB, 1))
  35. return token
  36. end
  37.  
  38. local function ontalk(inst, script)
  39. inst.SoundEmitter:PlaySound("dontstarve/pig/grunt")
  40. end
  41.  
  42. local function CalcSanityAura(inst, observer)
  43. return (inst.prefab == "moonpig" and -TUNING.SANITYAURA_LARGE)
  44. or (inst.components.werebeast ~= nil and inst.components.werebeast:IsInWereState() and -TUNING.SANITYAURA_LARGE)
  45. or (inst.components.follower ~= nil and inst.components.follower.leader == observer and TUNING.SANITYAURA_SMALL)
  46. or 0
  47. end
  48.  
  49. local function ShouldAcceptItem(inst, item)
  50. if item.components.equippable ~= nil and item.components.equippable.equipslot == EQUIPSLOTS.HEAD then
  51. return true
  52. elseif inst.components.eater:CanEat(item) then
  53. local foodtype = item.components.edible.foodtype
  54. if foodtype == FOODTYPE.MEAT or foodtype == FOODTYPE.HORRIBLE then
  55. return inst.components.follower.leader == nil or inst.components.follower:GetLoyaltyPercent() <= TUNING.PIG_FULL_LOYALTY_PERCENT
  56. elseif foodtype == FOODTYPE.VEGGIE or foodtype == FOODTYPE.RAW then
  57. local last_eat_time = inst.components.eater:TimeSinceLastEating()
  58. return (last_eat_time == nil or
  59. last_eat_time >= TUNING.PIG_MIN_POOP_PERIOD)
  60. and (inst.components.inventory == nil or
  61. not inst.components.inventory:Has(item.prefab, 1))
  62. end
  63. return true
  64. end
  65. end
  66.  
  67. local function OnGetItemFromPlayer(inst, giver, item)
  68. --I eat food
  69. if item.components.edible ~= nil then
  70. --meat makes us friends (unless I'm a guard)
  71. if ( item.components.edible.foodtype == FOODTYPE.MEAT or
  72. item.components.edible.foodtype == FOODTYPE.HORRIBLE
  73. ) and
  74. item.components.inventoryitem ~= nil and
  75. ( --make sure it didn't drop due to pockets full
  76. item.components.inventoryitem:GetGrandOwner() == inst or
  77. --could be merged into a stack
  78. ( not item:IsValid() and
  79. inst.components.inventory:FindItem(function(obj)
  80. return obj.prefab == item.prefab
  81. and obj.components.stackable ~= nil
  82. and obj.components.stackable:IsStack()
  83. end) ~= nil)
  84. ) then
  85. if inst.components.combat:TargetIs(giver) then
  86. inst.components.combat:SetTarget(nil)
  87. elseif giver.components.leader ~= nil and not (inst:HasTag("guard") or giver:HasTag("monster") or giver:HasTag("merm")) then
  88.  
  89. if giver.components.minigame_participator == nil then
  90. giver:PushEvent("makefriend")
  91. giver.components.leader:AddFollower(inst)
  92. end
  93. inst.components.follower:AddLoyaltyTime(item.components.edible:GetHunger() * TUNING.PIG_LOYALTY_PER_HUNGER)
  94. inst.components.follower.maxfollowtime =
  95. giver:HasTag("polite")
  96. and TUNING.PIG_LOYALTY_MAXTIME + TUNING.PIG_LOYALTY_POLITENESS_MAXTIME_BONUS
  97. or TUNING.PIG_LOYALTY_MAXTIME
  98. end
  99. end
  100. if inst.components.sleeper:IsAsleep() then
  101. inst.components.sleeper:WakeUp()
  102. end
  103. end
  104.  
  105. --I wear hats
  106. if item.components.equippable ~= nil and item.components.equippable.equipslot == EQUIPSLOTS.HEAD then
  107. local current = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HEAD)
  108. if current ~= nil then
  109. inst.components.inventory:DropItem(current)
  110. end
  111. inst.components.inventory:Equip(item)
  112. inst.AnimState:Show("hat")
  113. end
  114. end
  115.  
  116. local function OnRefuseItem(inst, item)
  117. inst.sg:GoToState("refuse")
  118. if inst.components.sleeper:IsAsleep() then
  119. inst.components.sleeper:WakeUp()
  120. end
  121. end
  122.  
  123. local function OnEat(inst, food)
  124. if food.components.edible ~= nil then
  125. if food.components.edible.foodtype == FOODTYPE.VEGGIE then
  126. SpawnPrefab("poop").Transform:SetPosition(inst.Transform:GetWorldPosition())
  127. elseif food.components.edible.foodtype == FOODTYPE.MEAT and
  128. inst.components.werebeast ~= nil and
  129. not inst.components.werebeast:IsInWereState() and
  130. food.components.edible:GetHealth(inst) < 0 then
  131. inst.components.werebeast:TriggerDelta(1)
  132. end
  133. end
  134. end
  135.  
  136. local SUGGESTTARGET_MUST_TAGS = { "_combat", "_health", "pig" }
  137. local SUGGESTTARGET_CANT_TAGS = { "werepig", "guard", "INLIMBO" }
  138.  
  139. local function OnAttackedByDecidRoot(inst, attacker)
  140. local x, y, z = inst.Transform:GetWorldPosition()
  141. local ents = TheSim:FindEntities(x, y, z, SpringCombatMod(SHARE_TARGET_DIST) * .5, SUGGESTTARGET_MUST_TAGS, SUGGESTTARGET_CANT_TAGS)
  142. local num_helpers = 0
  143. for i, v in ipairs(ents) do
  144. if v ~= inst and not v.components.health:IsDead() then
  145. v:PushEvent("suggest_tree_target", { tree = attacker })
  146. num_helpers = num_helpers + 1
  147. if num_helpers >= MAX_TARGET_SHARES then
  148. break
  149. end
  150. end
  151. end
  152. end
  153.  
  154. local function IsPig(dude)
  155. return dude:HasTag("pig")
  156. end
  157.  
  158. local function IsWerePig(dude)
  159. return dude:HasTag("werepig")
  160. end
  161.  
  162. local function IsNonWerePig(dude)
  163. return dude:HasTag("pig") and not dude:HasTag("werepig")
  164. end
  165.  
  166. local function IsGuardPig(dude)
  167. return dude:HasTag("guard") and dude:HasTag("pig")
  168. end
  169.  
  170. local function OnAttacked(inst, data)
  171. --print(inst, "OnAttacked")
  172. local attacker = data.attacker
  173. inst:ClearBufferedAction()
  174.  
  175. if attacker ~= nil then
  176. if attacker.prefab == "deciduous_root" and attacker.owner ~= nil then
  177. OnAttackedByDecidRoot(inst, attacker.owner)
  178. elseif attacker.prefab ~= "deciduous_root" and not attacker:HasTag("pigelite") then
  179. inst.components.combat:SetTarget(attacker)
  180.  
  181. if inst:HasTag("werepig") then
  182. inst.components.combat:ShareTarget(attacker, SHARE_TARGET_DIST, IsWerePig, MAX_TARGET_SHARES)
  183. elseif inst:HasTag("guard") then
  184. inst.components.combat:ShareTarget(attacker, SHARE_TARGET_DIST, attacker:HasTag("pig") and IsGuardPig or IsPig, MAX_TARGET_SHARES)
  185. elseif not (attacker:HasTag("pig") and attacker:HasTag("guard")) then
  186. inst.components.combat:ShareTarget(attacker, SHARE_TARGET_DIST, IsNonWerePig, MAX_TARGET_SHARES)
  187. end
  188. end
  189. end
  190. end
  191.  
  192. local function OnNewTarget(inst, data)
  193. if inst:HasTag("werepig") then
  194. inst.components.combat:ShareTarget(data.target, SHARE_TARGET_DIST, IsWerePig, MAX_TARGET_SHARES)
  195. end
  196. end
  197.  
  198. local builds = { "pig_build", "pigspotted_build" }
  199. local guardbuilds = { "pig_guard_build" }
  200. local RETARGET_MUST_TAGS = { "_combat" }
  201.  
  202. local function NormalRetargetFn(inst)
  203. local exclude_tags = { "playerghost", "INLIMBO" }
  204. if inst.components.follower.leader ~= nil then
  205. table.insert(exclude_tags, "abigail")
  206. end
  207. if inst.components.minigame_spectator ~= nil then
  208. table.insert(exclude_tags, "player") -- prevent spectators from auto-targeting webber
  209. end
  210.  
  211. local oneof_tags = {"monster"}
  212. if not inst:HasTag("merm") then
  213. table.insert(oneof_tags, "merm")
  214. end
  215.  
  216. return not inst:IsInLimbo()
  217. and FindEntity(
  218. inst,
  219. TUNING.PIG_TARGET_DIST,
  220. function(guy)
  221. return (guy.LightWatcher == nil or guy.LightWatcher:IsInLight())
  222. and inst.components.combat:CanTarget(guy)
  223. end,
  224. RETARGET_MUST_TAGS, -- see entityreplica.lua
  225. exclude_tags,
  226. oneof_tags
  227. )
  228. or nil
  229. end
  230.  
  231. local function NormalKeepTargetFn(inst, target)
  232. --give up on dead guys, or guys in the dark, or werepigs
  233. return inst.components.combat:CanTarget(target)
  234. and (target.LightWatcher == nil or target.LightWatcher:IsInLight())
  235. and not (target.sg ~= nil and target.sg:HasStateTag("transform"))
  236. end
  237.  
  238. local CAMPFIRE_TAGS = { "campfire", "fire" }
  239. local function NormalShouldSleep(inst)
  240. return DefaultSleepTest(inst)
  241. and (inst.components.follower == nil or inst.components.follower.leader == nil
  242. or (FindEntity(inst, 6, nil, CAMPFIRE_TAGS) ~= nil and
  243. (inst.LightWatcher == nil or inst.LightWatcher:IsInLight())))
  244. end
  245.  
  246. local normalbrain = require "brains/pigbrain"
  247.  
  248. local function SuggestTreeTarget(inst, data)
  249. if data ~= nil and data.tree ~= nil and inst:GetBufferedAction() ~= ACTIONS.CHOP then
  250. inst.tree_target = data.tree
  251. end
  252. end
  253.  
  254. local function OnItemGet(inst, data)
  255. if data.item ~= nil and data.item.prefab == PIG_TOKEN_PREFAB then
  256. inst.AnimState:OverrideSymbol("pig_belt", "pig_token", "pig_belt")
  257. --inst.AnimState:Show("belt")
  258. end
  259. end
  260.  
  261. local function OnItemLose(inst, data)
  262. if not inst.components.inventory:Has(PIG_TOKEN_PREFAB, 1) then
  263. inst.AnimState:ClearOverrideSymbol("pig_belt")
  264. --inst.AnimState:Hide("belt")
  265. end
  266. end
  267.  
  268. local function SetupPigToken(inst)
  269. if not inst._pigtokeninitialized then
  270. inst._pigtokeninitialized = true
  271. if math.random() <= (IsSpecialEventActive(SPECIAL_EVENTS.YOTP) and TUNING.PIG_TOKEN_CHANCE_YOTP or TUNING.PIG_TOKEN_CHANCE) then
  272. inst.components.inventory:GiveItem(SpawnPrefab(PIG_TOKEN_PREFAB))
  273. end
  274. end
  275. end
  276.  
  277. local function ReplacePigToken(inst)
  278. if inst._pigtokeninitialized then
  279. local item = GetPigToken(inst)
  280. local should_get_item = math.random() <= (IsSpecialEventActive(SPECIAL_EVENTS.YOTP) and TUNING.PIG_TOKEN_CHANCE_YOTP or TUNING.PIG_TOKEN_CHANCE)
  281. if item ~= nil and not should_get_item then
  282. inst.components.inventory:RemoveItem(item, true)
  283. item:Remove()
  284. elseif item == nil and should_get_item then
  285. inst.components.inventory:GiveItem(SpawnPrefab(PIG_TOKEN_PREFAB))
  286. end
  287. end
  288. end
  289.  
  290. local function SetNormalPig(inst)
  291. inst:RemoveTag("werepig")
  292. inst:RemoveTag("guard")
  293. inst:SetBrain(normalbrain)
  294. inst:SetStateGraph("SGpig")
  295. inst.AnimState:SetBuild(inst.build)
  296.  
  297. inst.components.sleeper:SetResistance(2)
  298.  
  299. inst.components.combat:SetDefaultDamage(TUNING.PIG_DAMAGE)
  300. inst.components.combat:SetAttackPeriod(TUNING.PIG_ATTACK_PERIOD)
  301. inst.components.combat:SetKeepTargetFunction(NormalKeepTargetFn)
  302. inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED
  303. inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED
  304.  
  305. inst.components.sleeper:SetSleepTest(NormalShouldSleep)
  306. inst.components.sleeper:SetWakeTest(DefaultWakeTest)
  307.  
  308. inst.components.lootdropper:SetLoot({})
  309. inst.components.lootdropper:AddRandomLoot("meat", 3)
  310. inst.components.lootdropper:AddRandomLoot("pigskin", 1)
  311. inst.components.lootdropper.numrandomloot = 1
  312.  
  313. inst.components.health:SetMaxHealth(TUNING.PIG_HEALTH)
  314. inst.components.combat:SetRetargetFunction(3, NormalRetargetFn)
  315. inst.components.combat:SetTarget(nil)
  316. inst:ListenForEvent("suggest_tree_target", SuggestTreeTarget)
  317.  
  318. inst.components.trader:Enable()
  319. inst.components.talker:StopIgnoringAll("becamewerepig")
  320. end
  321.  
  322. local KING_TAGS = { "king" }
  323. local RETARGET_GUARD_MUST_TAGS = { "character" }
  324. local RETARGET_GUARD_CANT_TAGS = { "guard", "INLIMBO" }
  325. local RETARGET_GUARD_PLAYER_MUST_TAGS = { "player" }
  326. local RETARGET_GUARD_LIMBO_CANT_TAGS = { "INLIMBO" }
  327.  
  328. local function GuardRetargetFn(inst)
  329. --defend the king, then the torch, then myself
  330. local home = inst.components.homeseeker ~= nil and inst.components.homeseeker.home or nil
  331. local defendDist = SpringCombatMod(TUNING.PIG_GUARD_DEFEND_DIST)
  332. local defenseTarget =
  333. FindEntity(inst, defendDist, nil, KING_TAGS) or
  334. (home ~= nil and inst:IsNear(home, defendDist) and home) or
  335. inst
  336.  
  337. if not defenseTarget.happy then
  338. local invader = FindEntity(defenseTarget, SpringCombatMod(TUNING.PIG_GUARD_TARGET_DIST), nil, RETARGET_GUARD_MUST_TAGS, RETARGET_GUARD_CANT_TAGS)
  339. if invader ~= nil and
  340. not (defenseTarget.components.trader ~= nil and defenseTarget.components.trader:IsTryingToTradeWithMe(invader)) and
  341. not (inst.components.trader ~= nil and inst.components.trader:IsTryingToTradeWithMe(invader)) then
  342. return invader
  343. end
  344.  
  345. if not TheWorld.state.isday and home ~= nil and home.components.burnable ~= nil and home.components.burnable:IsBurning() then
  346. local lightThief = FindEntity(
  347. home,
  348. home.components.burnable:GetLargestLightRadius(),
  349. function(guy)
  350. return guy.LightWatcher:IsInLight()
  351. and not (defenseTarget.components.trader ~= nil and defenseTarget.components.trader:IsTryingToTradeWithMe(guy))
  352. and not (inst.components.trader ~= nil and inst.components.trader:IsTryingToTradeWithMe(guy))
  353. end,
  354. RETARGET_GUARD_PLAYER_MUST_TAGS
  355. )
  356. if lightThief ~= nil then
  357. return lightThief
  358. end
  359. end
  360. end
  361.  
  362. local oneof_tags = {"monster"}
  363. if not inst:HasTag("merm") then
  364. table.insert(oneof_tags, "merm")
  365. end
  366.  
  367. return FindEntity(defenseTarget, defendDist, nil, {}, RETARGET_GUARD_LIMBO_CANT_TAGS, oneof_tags)
  368. end
  369.  
  370. local function GuardKeepTargetFn(inst, target)
  371. if not inst.components.combat:CanTarget(target) or
  372. (target.sg ~= nil and target.sg:HasStateTag("transform")) or
  373. (target:HasTag("guard") and target:HasTag("pig")) then
  374. return false
  375. end
  376.  
  377. local home = inst.components.homeseeker ~= nil and inst.components.homeseeker.home or nil
  378. if home == nil then
  379. return true
  380. end
  381.  
  382. local defendDist = not TheWorld.state.isday
  383. and home.components.burnable ~= nil
  384. and home.components.burnable:IsBurning()
  385. and home.components.burnable:GetLargestLightRadius()
  386. or SpringCombatMod(TUNING.PIG_GUARD_DEFEND_DIST)
  387. return target:IsNear(home, defendDist) and inst:IsNear(home, defendDist)
  388. end
  389.  
  390. local function GuardShouldSleep(inst)
  391. return false
  392. end
  393.  
  394. local function GuardShouldWake(inst)
  395. return true
  396. end
  397.  
  398. local guardbrain = require "brains/pigguardbrain"
  399.  
  400. local function SetGuardPig(inst)
  401. inst:RemoveTag("werepig")
  402. inst:AddTag("guard")
  403. inst:SetBrain(guardbrain)
  404. inst:SetStateGraph("SGpig")
  405. inst.AnimState:SetBuild(inst.build)
  406.  
  407. inst.components.sleeper:SetResistance(3)
  408.  
  409. inst.components.health:SetMaxHealth(TUNING.PIG_GUARD_HEALTH)
  410. inst.components.combat:SetDefaultDamage(TUNING.PIG_GUARD_DAMAGE)
  411. inst.components.combat:SetAttackPeriod(TUNING.PIG_GUARD_ATTACK_PERIOD)
  412. inst.components.combat:SetKeepTargetFunction(GuardKeepTargetFn)
  413. inst.components.combat:SetRetargetFunction(1, GuardRetargetFn)
  414. inst.components.combat:SetTarget(nil)
  415. inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED
  416. inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED
  417.  
  418. inst.components.sleeper:SetSleepTest(GuardShouldSleep)
  419. inst.components.sleeper:SetWakeTest(GuardShouldWake)
  420.  
  421. inst.components.lootdropper:SetLoot({})
  422. inst.components.lootdropper:AddRandomLoot("meat", 3)
  423. inst.components.lootdropper:AddRandomLoot("pigskin", 1)
  424. inst.components.lootdropper.numrandomloot = 1
  425.  
  426. inst.components.trader:Enable()
  427. inst.components.talker:StopIgnoringAll("becamewerepig")
  428. inst.components.follower:SetLeader(nil)
  429. end
  430.  
  431. local RETARGET_MUST_TAGS = { "_combat" }
  432. local WEREPIG_RETARGET_CANT_TAGS = { "werepig", "alwaysblock", "wereplayer" }
  433. local function WerepigRetargetFn(inst)
  434. return FindEntity(
  435. inst,
  436. SpringCombatMod(TUNING.PIG_TARGET_DIST),
  437. function(guy)
  438. return inst.components.combat:CanTarget(guy)
  439. and not (guy.sg ~= nil and guy.sg:HasStateTag("transform"))
  440. end,
  441. RETARGET_MUST_TAGS, --See entityreplica.lua (re: "_combat" tag)
  442. WEREPIG_RETARGET_CANT_TAGS
  443. )
  444. end
  445.  
  446. local function WerepigKeepTargetFn(inst, target)
  447. return inst.components.combat:CanTarget(target)
  448. and not target:HasTag("werepig")
  449. and not target:HasTag("wereplayer")
  450. and not (target.sg ~= nil and target.sg:HasStateTag("transform"))
  451. end
  452.  
  453. local function IsNearMoonBase(inst, dist)
  454. local moonbase = inst.components.entitytracker:GetEntity("moonbase")
  455. return moonbase == nil or inst:IsNear(moonbase, dist)
  456. end
  457.  
  458. local MOONPIG_RETARGET_CANT_TAGS = { "werepig", "alwaysblock", "wereplayer", "moonbeast" }
  459. local function MoonpigRetargetFn(inst)
  460. return IsNearMoonBase(inst, TUNING.MOONPIG_AGGRO_DIST)
  461. and FindEntity(
  462. inst,
  463. TUNING.PIG_TARGET_DIST,
  464. function(guy)
  465. return inst.components.combat:CanTarget(guy)
  466. and not (guy.sg ~= nil and guy.sg:HasStateTag("transform"))
  467. end,
  468. RETARGET_MUST_TAGS, --See entityreplica.lua (re: "_combat" tag)
  469. MOONPIG_RETARGET_CANT_TAGS
  470. )
  471. or nil
  472. end
  473.  
  474. local function MoonpigKeepTargetFn(inst, target)
  475. return IsNearMoonBase(inst, TUNING.MOONPIG_RETURN_DIST)
  476. and not target:HasTag("moonbeast")
  477. and WerepigKeepTargetFn(inst, target)
  478. end
  479.  
  480. local function WerepigSleepTest(inst)
  481. return false
  482. end
  483.  
  484. local function WerepigWakeTest(inst)
  485. return true
  486. end
  487.  
  488. local werepigbrain = require "brains/werepigbrain"
  489.  
  490. local function SetWerePig(inst)
  491. inst:AddTag("werepig")
  492. inst:RemoveTag("guard")
  493. inst:SetBrain(werepigbrain)
  494. inst:SetStateGraph("SGwerepig")
  495. inst.AnimState:SetBuild("werepig_build")
  496.  
  497. inst.components.sleeper:SetResistance(3)
  498.  
  499. inst.components.combat:SetDefaultDamage(TUNING.WEREPIG_DAMAGE)
  500. inst.components.combat:SetAttackPeriod(TUNING.WEREPIG_ATTACK_PERIOD)
  501. inst.components.locomotor.runspeed = TUNING.WEREPIG_RUN_SPEED
  502. inst.components.locomotor.walkspeed = TUNING.WEREPIG_WALK_SPEED
  503.  
  504. inst.components.sleeper:SetSleepTest(WerepigSleepTest)
  505. inst.components.sleeper:SetWakeTest(WerepigWakeTest)
  506.  
  507. inst.components.lootdropper:SetLoot({ "meat", "meat", "pigskin" })
  508. inst.components.lootdropper.numrandomloot = 0
  509.  
  510. inst.components.health:SetMaxHealth(TUNING.WEREPIG_HEALTH)
  511. inst.components.combat:SetTarget(nil)
  512. inst.components.combat:SetRetargetFunction(3, WerepigRetargetFn)
  513. inst.components.combat:SetKeepTargetFunction(WerepigKeepTargetFn)
  514.  
  515. inst.components.trader:Disable()
  516. inst.components.follower:SetLeader(nil)
  517. inst.components.talker:IgnoreAll("becamewerepig")
  518. end
  519.  
  520. local function GetStatus(inst)
  521. return (inst:HasTag("werepig") and "WEREPIG")
  522. or (inst:HasTag("guard") and "GUARD")
  523. or (inst.components.follower.leader ~= nil and "FOLLOWER")
  524. or nil
  525. end
  526.  
  527. local function displaynamefn(inst)
  528. return inst.name
  529. end
  530.  
  531. local function OnSave(inst, data)
  532. data.build = inst.build
  533. data._pigtokeninitialized = inst._pigtokeninitialized
  534. end
  535.  
  536. local function OnLoad(inst, data)
  537. if data ~= nil then
  538. inst.build = data.build or builds[1]
  539. if not inst.components.werebeast:IsInWereState() then
  540. inst.AnimState:SetBuild(inst.build)
  541. end
  542. inst._pigtokeninitialized = data._pigtokeninitialized
  543. end
  544. end
  545.  
  546. local function CustomOnHaunt(inst)
  547. if not inst:HasTag("werepig") and math.random() <= TUNING.HAUNT_CHANCE_OCCASIONAL then
  548. local remainingtime = TUNING.TOTAL_DAY_TIME * (1 - TheWorld.state.time)
  549. local mintime = TUNING.SEG_TIME
  550. inst.components.werebeast:SetWere(math.max(mintime, remainingtime) + math.random() * TUNING.SEG_TIME)
  551. inst.components.hauntable.hauntvalue = TUNING.HAUNT_LARGE
  552. end
  553. end
  554.  
  555. local function common(moonbeast)
  556. local inst = CreateEntity()
  557.  
  558. inst.entity:AddTransform()
  559. inst.entity:AddAnimState()
  560. inst.entity:AddSoundEmitter()
  561. inst.entity:AddDynamicShadow()
  562. inst.entity:AddLightWatcher()
  563. inst.entity:AddNetwork()
  564.  
  565. MakeCharacterPhysics(inst, 50, .5)
  566.  
  567. inst.DynamicShadow:SetSize(1.5, .75)
  568. inst.Transform:SetFourFaced()
  569.  
  570. inst:AddTag("character")
  571. inst:AddTag("pig")
  572. inst:AddTag("scarytoprey")
  573. inst.AnimState:SetBank("pigman")
  574. inst.AnimState:PlayAnimation("idle_loop", true)
  575. inst.AnimState:Hide("hat")
  576.  
  577. --Sneak these into pristine state for optimization
  578. inst:AddTag("_named")
  579.  
  580. if moonbeast then
  581. inst:AddTag("werepig")
  582. inst:AddTag("moonbeast")
  583. inst:AddTag("hostile")
  584. inst.AnimState:SetBuild("werepig_build")
  585. --Since we override prefab name, we will need to use the higher
  586. --priority displaynamefn to return us back plain old .name LOL!
  587. inst:SetPrefabNameOverride("pigman")
  588. inst.displaynamefn = displaynamefn
  589.  
  590. inst:AddComponent("spawnfader")
  591. else
  592. --trader (from trader component) added to pristine state for optimization
  593. inst:AddTag("trader")
  594.  
  595. inst:AddComponent("talker")
  596. inst.components.talker.fontsize = 35
  597. inst.components.talker.font = TALKINGFONT
  598. --inst.components.talker.colour = Vector3(133/255, 140/255, 167/255)
  599. inst.components.talker.offset = Vector3(0, -400, 0)
  600. inst.components.talker:MakeChatter()
  601. end
  602.  
  603. inst.entity:SetPristine()
  604.  
  605. if not TheWorld.ismastersim then
  606. return inst
  607. end
  608.  
  609. --Remove these tags so that they can be added properly when replicating components below
  610. inst:RemoveTag("_named")
  611.  
  612. if not moonbeast then
  613. inst.components.talker.ontalk = ontalk
  614.  
  615. inst._pig_token_prefab = PIG_TOKEN_PREFAB
  616. inst:ListenForEvent("onvacatehome", ReplacePigToken)
  617. inst:ListenForEvent("itemget", OnItemGet)
  618. inst:ListenForEvent("itemlose", OnItemLose)
  619. end
  620.  
  621. inst:AddComponent("locomotor") -- locomotor must be constructed before the stategraph
  622. inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED --5
  623. inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED --3
  624.  
  625. inst:AddComponent("bloomer")
  626.  
  627. ------------------------------------------
  628. inst:AddComponent("eater")
  629. inst.components.eater:SetDiet({ FOODGROUP.OMNI }, { FOODGROUP.OMNI })
  630. inst.components.eater:SetCanEatHorrible()
  631. inst.components.eater:SetCanEatRaw()
  632. inst.components.eater.strongstomach = true -- can eat monster meat!
  633. inst.components.eater:SetOnEatFn(OnEat)
  634. ------------------------------------------
  635. inst:AddComponent("health")
  636. inst:AddComponent("combat")
  637. inst.components.combat.hiteffectsymbol = "pig_torso"
  638.  
  639. MakeMediumBurnableCharacter(inst, "pig_torso")
  640.  
  641. inst:AddComponent("named")
  642. inst.components.named.possiblenames = STRINGS.PIGNAMES
  643. inst.components.named:PickNewName()
  644.  
  645. ------------------------------------------
  646. MakeHauntablePanic(inst)
  647.  
  648. if not moonbeast then
  649. inst:AddComponent("werebeast")
  650. inst.components.werebeast:SetOnWereFn(SetWerePig)
  651. inst.components.werebeast:SetTriggerLimit(4)
  652.  
  653. AddHauntableCustomReaction(inst, CustomOnHaunt, true, nil, true)
  654. end
  655.  
  656. ------------------------------------------
  657. inst:AddComponent("follower")
  658. inst.components.follower.maxfollowtime = TUNING.PIG_LOYALTY_MAXTIME
  659. ------------------------------------------
  660.  
  661. inst:AddComponent("inventory")
  662.  
  663. ------------------------------------------
  664.  
  665. inst:AddComponent("lootdropper")
  666.  
  667. ------------------------------------------
  668.  
  669. inst:AddComponent("knownlocations")
  670.  
  671. ------------------------------------------
  672.  
  673. if not moonbeast then
  674. inst:AddComponent("trader")
  675. inst.components.trader:SetAcceptTest(ShouldAcceptItem)
  676. inst.components.trader.onaccept = OnGetItemFromPlayer
  677. inst.components.trader.onrefuse = OnRefuseItem
  678. inst.components.trader.deleteitemonaccept = false
  679. end
  680.  
  681. ------------------------------------------
  682.  
  683. inst:AddComponent("sanityaura")
  684. inst.components.sanityaura.aurafn = CalcSanityAura
  685.  
  686. ------------------------------------------
  687.  
  688. inst:AddComponent("sleeper")
  689.  
  690. ------------------------------------------
  691. MakeMediumFreezableCharacter(inst, "pig_torso")
  692.  
  693. ------------------------------------------
  694.  
  695. inst:AddComponent("inspectable")
  696. inst.components.inspectable.getstatus = GetStatus
  697. ------------------------------------------
  698.  
  699. if not moonbeast then
  700. inst.OnSave = OnSave
  701. inst.OnLoad = OnLoad
  702. end
  703.  
  704. inst:ListenForEvent("attacked", OnAttacked)
  705. inst:ListenForEvent("newcombattarget", OnNewTarget)
  706.  
  707. return inst
  708. end
  709.  
  710. local function normal()
  711. local inst = common(false)
  712.  
  713. if not TheWorld.ismastersim then
  714. return inst
  715. end
  716.  
  717. -- boat hopping setup
  718. inst.components.locomotor:SetAllowPlatformHopping(true)
  719. inst:AddComponent("embarker")
  720. inst:AddComponent("drownable")
  721.  
  722. inst.build = builds[math.random(#builds)]
  723. inst.AnimState:SetBuild(inst.build)
  724. inst.components.werebeast:SetOnNormalFn(SetNormalPig)
  725. SetNormalPig(inst)
  726.  
  727. inst:DoTaskInTime(0, SetupPigToken)
  728. return inst
  729. end
  730.  
  731. local function guard()
  732. local inst = common(false)
  733.  
  734. if not TheWorld.ismastersim then
  735. return inst
  736. end
  737.  
  738. -- boat hopping setup
  739. inst.components.locomotor:SetAllowPlatformHopping(true)
  740. inst:AddComponent("embarker")
  741. inst:AddComponent("drownable")
  742.  
  743. inst.build = guardbuilds[math.random(#guardbuilds)]
  744. inst.AnimState:SetBuild(inst.build)
  745. inst.components.werebeast:SetOnNormalFn(SetGuardPig)
  746. SetGuardPig(inst)
  747. return inst
  748. end
  749.  
  750. local gargoyles =
  751. {
  752. "gargoyle_werepigatk",
  753. "gargoyle_werepigdeath",
  754. "gargoyle_werepighowl",
  755. }
  756. local moonpigprefabs = {}
  757. for i, v in ipairs(gargoyles) do
  758. table.insert(moonpigprefabs, v)
  759. end
  760. for i, v in ipairs(prefabs) do
  761. table.insert(moonpigprefabs, v)
  762. end
  763.  
  764. local moonbeastbrain = require "brains/moonbeastbrain"
  765.  
  766. local function OnMoonPetrify(inst)
  767. if not inst.components.health:IsDead() and (not inst.sg:HasStateTag("busy") or inst:IsAsleep()) then
  768. local x, y, z = inst.Transform:GetWorldPosition()
  769. local rot = inst.Transform:GetRotation()
  770. local name = inst.components.named.name
  771. inst:Remove()
  772. local gargoyle = SpawnPrefab(gargoyles[math.random(#gargoyles)])
  773. gargoyle.components.named:SetName(name)
  774. gargoyle.Transform:SetPosition(x, y, z)
  775. gargoyle.Transform:SetRotation(rot)
  776. gargoyle:Petrify()
  777. end
  778. end
  779.  
  780. local function OnMoonTransformed(inst, data)
  781. inst.components.named:SetName(data.old.components.named.name)
  782. inst.sg:GoToState("howl")
  783. end
  784.  
  785. local function moon()
  786. local inst = common(true)
  787.  
  788. if not TheWorld.ismastersim then
  789. return inst
  790. end
  791.  
  792. inst:AddComponent("entitytracker")
  793.  
  794. inst:SetBrain(moonbeastbrain)
  795. inst:SetStateGraph("SGmoonpig")
  796.  
  797. inst.components.sleeper:SetResistance(3)
  798. inst.components.freezable:SetDefaultWearOffTime(TUNING.MOONPIG_FREEZE_WEAR_OFF_TIME)
  799.  
  800. inst.components.combat:SetDefaultDamage(TUNING.WEREPIG_DAMAGE)
  801. inst.components.combat:SetAttackPeriod(TUNING.WEREPIG_ATTACK_PERIOD)
  802. inst.components.locomotor.runspeed = TUNING.WEREPIG_RUN_SPEED
  803. inst.components.locomotor.walkspeed = TUNING.WEREPIG_WALK_SPEED
  804.  
  805. inst.components.sleeper:SetSleepTest(WerepigSleepTest)
  806. inst.components.sleeper:SetWakeTest(WerepigWakeTest)
  807.  
  808. inst.components.lootdropper:SetLoot({ "meat", "meat", "pigskin" })
  809. inst.components.lootdropper.numrandomloot = 0
  810.  
  811. inst.components.health:SetMaxHealth(TUNING.WEREPIG_HEALTH)
  812. inst.components.combat:SetTarget(nil)
  813. inst.components.combat:SetRetargetFunction(3, MoonpigRetargetFn)
  814. inst.components.combat:SetKeepTargetFunction(MoonpigKeepTargetFn)
  815.  
  816. inst:ListenForEvent("moonpetrify", OnMoonPetrify)
  817. inst:ListenForEvent("moontransformed", OnMoonTransformed)
  818.  
  819. return inst
  820. end
  821.  
  822. return Prefab("pigman", normal, assets, prefabs),
  823. Prefab("pigguard", guard, assets, prefabs),
  824. Prefab("moonpig", moon, assets, moonpigprefabs)
Add Comment
Please, Sign In to add comment