mirror of
https://github.com/solero/houdini.git
synced 2024-11-21 21:17:21 +00:00
Puffle handlers
This commit is contained in:
parent
2f6c53c872
commit
909cb88a21
110
houdini.sql
110
houdini.sql
@ -275,18 +275,18 @@ COMMENT ON COLUMN puffle_item.clean_effect IS 'Effect on puffle clean level';
|
||||
DROP TABLE IF EXISTS puffle;
|
||||
CREATE TABLE puffle (
|
||||
id INT NOT NULL,
|
||||
parent_id SMALLINT NOT NULL,
|
||||
parent_id SMALLINT DEFAULT NULL,
|
||||
name VARCHAR(50) NOT NULL DEFAULT '',
|
||||
cost INT NOT NULL DEFAULT 0,
|
||||
member BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
favourite_food SMALLINT NOT NULL,
|
||||
favourite_toy SMALLINT DEFAULT NULL,
|
||||
runaway_postcard SMALLINT DEFAULT NULL,
|
||||
max_food SMALLINT NOT NULL DEFAULT 100,
|
||||
max_rest SMALLINT NOT NULL DEFAULT 100,
|
||||
max_clean SMALLINT NOT NULL DEFAULT 100,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT puffle_ibfk_1 FOREIGN KEY (parent_id) REFERENCES puffle (id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
CONSTRAINT puffle_ibfk_2 FOREIGN KEY (favourite_food) REFERENCES puffle_item (id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
CONSTRAINT puffle_ibfk_3 FOREIGN KEY (runaway_postcard) REFERENCES postcard (id) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
CONSTRAINT puffle_ibfk_3 FOREIGN KEY (favourite_toy) REFERENCES puffle_item (id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
CONSTRAINT puffle_ibfk_4 FOREIGN KEY (runaway_postcard) REFERENCES postcard (id) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
COMMENT ON TABLE puffle IS 'Server puffle crumbs';
|
||||
@ -294,12 +294,10 @@ COMMENT ON TABLE puffle IS 'Server puffle crumbs';
|
||||
COMMENT ON COLUMN puffle.id IS 'Unique puffle ID';
|
||||
COMMENT ON COLUMN puffle.parent_id IS 'Base color puffle ID';
|
||||
COMMENT ON COLUMN puffle.name IS 'Puffle name';
|
||||
COMMENT ON COLUMN puffle.cost IS 'Puffle cost';
|
||||
COMMENT ON COLUMN puffle.member IS 'Is member-only?';
|
||||
COMMENT ON COLUMN puffle.favourite_food IS 'Favourite puffle-care item';
|
||||
COMMENT ON COLUMN puffle.runaway_postcard IS 'Runaway postcard ID';
|
||||
COMMENT ON COLUMN puffle.max_food IS 'Maximum food level';
|
||||
COMMENT ON COLUMN puffle.max_rest IS 'Maximum rest level';
|
||||
COMMENT ON COLUMN puffle.max_clean IS 'Maximum clean level';
|
||||
|
||||
DROP TABLE IF EXISTS puffle_treasure_item;
|
||||
CREATE TABLE puffle_treasure_item (
|
||||
@ -520,9 +518,11 @@ CREATE TABLE penguin (
|
||||
ninja_matches_won INT NOT NULL DEFAULT 0,
|
||||
fire_matches_won INT NOT NULL DEFAULT 0,
|
||||
water_matches_won INT NOT NULL DEFAULT 0,
|
||||
rainbow_adoptability SMALLINT NOT NULL DEFAULT 0,
|
||||
rainbow_adoptability BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
has_dug BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
puffle_handler BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
nuggets SMALLINT NOT NULL DEFAULT 0,
|
||||
walking INT DEFAULT NULL,
|
||||
opened_playercard BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
special_wave BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
special_dance BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
@ -616,7 +616,9 @@ COMMENT ON COLUMN penguin.fire_matches_won IS 'JitsuFire matches won';
|
||||
COMMENT ON COLUMN penguin.water_matches_won IS 'JitsuWater matces won';
|
||||
COMMENT ON COLUMN penguin.rainbow_adoptability IS 'Rainbow puffle adoptability status';
|
||||
COMMENT ON COLUMN penguin.has_dug IS 'Puffle digging boolean';
|
||||
COMMENT ON COLUMN penguin.puffle_handler IS 'Has met puffle handler?';
|
||||
COMMENT ON COLUMN penguin.nuggets IS 'Golden puffle nuggets';
|
||||
COMMENT ON COLUMN penguin.walking IS 'Walking puffle ID';
|
||||
COMMENT ON COLUMN penguin.opened_playercard IS 'Has player opened playercard?';
|
||||
COMMENT ON COLUMN penguin.map_category IS 'Currently selected map category';
|
||||
COMMENT ON COLUMN penguin.status_field IS 'New player status field';
|
||||
@ -880,7 +882,7 @@ CREATE TABLE penguin_igloo_room (
|
||||
flooring INT NOT NULL,
|
||||
music SMALLINT NOT NULL DEFAULT 0,
|
||||
location INT NOT NULL,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT igloo_room_ibfk_1 FOREIGN KEY (penguin_id) REFERENCES penguin (id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
CONSTRAINT igloo_room_ibfk_2 FOREIGN KEY (type) REFERENCES igloo (id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
@ -1111,7 +1113,6 @@ CREATE TABLE penguin_puffle (
|
||||
play SMALLINT NOT NULL DEFAULT 100,
|
||||
rest SMALLINT NOT NULL DEFAULT 100,
|
||||
clean SMALLINT NOT NULL DEFAULT 100,
|
||||
walking BOOLEAN DEFAULT FALSE,
|
||||
hat INT DEFAULT NULL,
|
||||
backyard BOOLEAN DEFAULT FALSE,
|
||||
has_dug BOOLEAN DEFAULT FALSE,
|
||||
@ -1134,11 +1135,12 @@ COMMENT ON COLUMN penguin_puffle.food IS 'Puffle health %';
|
||||
COMMENT ON COLUMN penguin_puffle.play IS 'Puffle hunger %';
|
||||
COMMENT ON COLUMN penguin_puffle.rest IS 'Puffle rest %';
|
||||
COMMENT ON COLUMN penguin_puffle.clean IS 'Puffle clean %';
|
||||
COMMENT ON COLUMN penguin_puffle.walking IS 'Is being walked?';
|
||||
COMMENT ON COLUMN penguin_puffle.hat IS 'Puffle hat item ID';
|
||||
COMMENT ON COLUMN penguin_puffle.backyard IS 'Is in backyard?';
|
||||
COMMENT ON COLUMN penguin_puffle.has_dug IS 'Has dug?';
|
||||
|
||||
ALTER TABLE penguin ADD CONSTRAINT penguin_ibfk_12 FOREIGN KEY (walking) REFERENCES penguin_puffle (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
DROP TABLE IF EXISTS penguin_puffle_item;
|
||||
CREATE TABLE penguin_puffle_item (
|
||||
penguin_id INT NOT NULL,
|
||||
@ -9102,47 +9104,47 @@ INSERT INTO puffle_item (id, parent_id, name, type, play_external, cost, quantit
|
||||
(158, 158, 'Drum Roll', 'head', 'none', 0, 1, TRUE, 0, 0, 0, 0),
|
||||
(159, 159, 'Swashbuckler Hat', 'head', 'none', 0, 1, TRUE, 0, 0, 0, 0);
|
||||
|
||||
INSERT INTO puffle (id, parent_id, name, member, favourite_food, runaway_postcard, max_food, max_rest, max_clean) VALUES
|
||||
(0, 0, 'Blue', FALSE, 101, 100, 100, 100, 100),
|
||||
(1, 1, 'Pink', TRUE, 107, 101, 100, 120, 80),
|
||||
(2, 2, 'Black', TRUE, 112, 102, 120, 80, 100),
|
||||
(3, 3, 'Green', TRUE, 109, 103, 80, 100, 120),
|
||||
(4, 4, 'Purple', TRUE, 110, 104, 80, 120, 80),
|
||||
(5, 5, 'Red', FALSE, 106, 105, 100, 80, 120),
|
||||
(6, 6, 'Yellow', TRUE, 114, 106, 100, 100, 100),
|
||||
(7, 7, 'White', TRUE, 111, 169, 120, 80, 100),
|
||||
(8, 8, 'Orange', TRUE, 108, 109, 100, 80, 120),
|
||||
(9, 9, 'Brown', TRUE, 113, NULL, 100, 100, 100),
|
||||
(10, 10, 'Rainbow', TRUE, 115, NULL, 100, 100, 100),
|
||||
(11, 11, 'Gold', TRUE, 128, NULL, 100, 100, 100),
|
||||
(1000, 2, 'Black T-Rex', TRUE, 112, NULL, 100, 100, 100),
|
||||
(1001, 4, 'Purple T-Rex', TRUE, 110, NULL, 100, 100, 100),
|
||||
(1002, 5, 'Red Triceratops', TRUE, 106, NULL, 100, 100, 100),
|
||||
(1003, 0, 'Blue Triceratops', TRUE, 101, NULL, 100, 100, 100),
|
||||
(1004, 6, 'Yellow Stegasaurus', TRUE, 114, NULL, 100, 100, 100),
|
||||
(1005, 1, 'Pink Stegasaurus', TRUE, 107, NULL, 100, 100, 100),
|
||||
(1006, 0, 'Blue Dog', TRUE, 101, NULL, 100, 100, 100),
|
||||
(1007, 8, 'Orange Cat', TRUE, 108, NULL, 100, 100, 100),
|
||||
(1008, 3, 'Green Raccoon', TRUE, 109, NULL, 100, 100, 100),
|
||||
(1009, 8, 'Orange Raccoon', TRUE, 108, NULL, 100, 100, 100),
|
||||
(1010, 1, 'Pink Raccoon', TRUE, 107, NULL, 100, 100, 100),
|
||||
(1011, 0, 'Blue Raccoon', TRUE, 101, NULL, 100, 100, 100),
|
||||
(1012, 3, 'Green Rabbit', TRUE, 109, NULL, 100, 100, 100),
|
||||
(1013, 1, 'Pink Rabbit', TRUE, 107, NULL, 100, 100, 100),
|
||||
(1014, 7, 'White Rabbit', TRUE, 111, NULL, 100, 100, 100),
|
||||
(1015, 5, 'Red Rabbit', TRUE, 106, NULL, 100, 100, 100),
|
||||
(1016, 0, 'Blue Deer', TRUE, 101, NULL, 100, 100, 100),
|
||||
(1017, 2, 'Black Deer', TRUE, 112, NULL, 100, 100, 100),
|
||||
(1018, 5, 'Red Deer', TRUE, 106, NULL, 100, 100, 100),
|
||||
(1019, 4, 'Purple Deer', TRUE, 110, NULL, 100, 100, 100),
|
||||
(1020, 6, 'Yellow Unicorn', TRUE, 114, NULL, 100, 100, 100),
|
||||
(1021, 7, 'Snowman', TRUE, 111, NULL, 100, 100, 100),
|
||||
(1022, 4, 'Ghost', TRUE, 110, NULL, 100, 100, 100),
|
||||
(1023, 0, 'Crystal', TRUE, 101, NULL, 100, 100, 100),
|
||||
(1024, 3, 'Green Alien', FALSE, 109, NULL, 100, 100, 100),
|
||||
(1025, 8, 'Orange Alien', TRUE, 108, NULL, 100, 100, 100),
|
||||
(1026, 6, 'Yellow Alien', TRUE, 114, NULL, 100, 100, 100),
|
||||
(1027, 4, 'Purple Alien', TRUE, 110, NULL, 100, 100, 100);
|
||||
INSERT INTO puffle (id, parent_id, name, cost, member, favourite_food, favourite_toy, runaway_postcard) VALUES
|
||||
(0, NULL, 'Blue', 400, FALSE, 101, 27, 100),
|
||||
(1, NULL, 'Pink', 400, TRUE, 107, 28, 101),
|
||||
(2, NULL, 'Black', 400, TRUE, 112, 31, 102),
|
||||
(3, NULL, 'Green', 400, TRUE, 109, 30, 103),
|
||||
(4, NULL, 'Purple', 400, TRUE, 110, 35, 104),
|
||||
(5, NULL, 'Red', 400, FALSE, 106, 29, 105),
|
||||
(6, NULL, 'Yellow', 400, TRUE, 114, 32, 106),
|
||||
(7, NULL, 'White', 400, TRUE, 111, 33, 169),
|
||||
(8, NULL, 'Orange', 400, TRUE, 108, 34, 109),
|
||||
(9, NULL, 'Brown', 400, TRUE, 113, 36, NULL),
|
||||
(10, NULL, 'Rainbow', 0, TRUE, 115, 103, NULL),
|
||||
(11, NULL, 'Gold', 0, TRUE, 128, 125, NULL),
|
||||
(1000, 2, 'Black T-Rex', 0, TRUE, 112, NULL, NULL),
|
||||
(1001, 4, 'Purple T-Rex', 0, TRUE, 110, NULL, NULL),
|
||||
(1002, 5, 'Red Triceratops', 0, TRUE, 106, NULL, NULL),
|
||||
(1003, 0, 'Blue Triceratops', 0, TRUE, 101, NULL, NULL),
|
||||
(1004, 6, 'Yellow Stegasaurus', 0, TRUE, 114, NULL, NULL),
|
||||
(1005, 1, 'Pink Stegasaurus', 0, TRUE, 107, NULL, NULL),
|
||||
(1006, 0, 'Blue Dog', 800, TRUE, 101, NULL, NULL),
|
||||
(1007, 8, 'Orange Cat', 800, TRUE, 108, NULL, NULL),
|
||||
(1008, 3, 'Green Raccoon', 800, TRUE, 109, NULL, NULL),
|
||||
(1009, 8, 'Orange Raccoon', 800, TRUE, 108, NULL, NULL),
|
||||
(1010, 1, 'Pink Raccoon', 800, TRUE, 107, NULL, NULL),
|
||||
(1011, 0, 'Blue Raccoon', 800, TRUE, 101, NULL, NULL),
|
||||
(1012, 3, 'Green Rabbit', 800, TRUE, 109, NULL, NULL),
|
||||
(1013, 1, 'Pink Rabbit', 800, TRUE, 107, NULL, NULL),
|
||||
(1014, 7, 'White Rabbit', 800, TRUE, 111, NULL, NULL),
|
||||
(1015, 5, 'Red Rabbit', 800, TRUE, 106, NULL, NULL),
|
||||
(1016, 0, 'Blue Deer', 800, TRUE, 101, NULL, NULL),
|
||||
(1017, 2, 'Black Deer', 800, TRUE, 112, NULL, NULL),
|
||||
(1018, 5, 'Red Deer', 800, TRUE, 106, NULL, NULL),
|
||||
(1019, 4, 'Purple Deer', 800, TRUE, 110, NULL, NULL),
|
||||
(1020, 6, 'Yellow Unicorn', 800, TRUE, 114, NULL, NULL),
|
||||
(1021, 7, 'Snowman', 0, TRUE, 111, NULL, NULL),
|
||||
(1022, 4, 'Ghost', 0, TRUE, 110, NULL, NULL),
|
||||
(1023, 0, 'Crystal', 0, TRUE, 101, NULL, NULL),
|
||||
(1024, 3, 'Green Alien', 0, FALSE, 109, NULL, NULL),
|
||||
(1025, 8, 'Orange Alien', 0, TRUE, 108, NULL, NULL),
|
||||
(1026, 6, 'Yellow Alien', 0, TRUE, 114, NULL, NULL),
|
||||
(1027, 4, 'Purple Alien', 0, TRUE, 110, NULL, NULL);
|
||||
|
||||
INSERT INTO puffle_treasure_puffle_item (puffle_id, puffle_item_id) VALUES
|
||||
(0, 115), (1, 115), (2, 115), (3, 115), (4, 115), (5, 115), (6, 115), (7, 115), (8, 115), (9, 115), (10, 115), (11, 115), (1000, 115), (1001, 115), (1002, 115), (1003, 115), (1004, 115), (1005, 115), (1006, 115), (1007, 115), (1008, 115), (1009, 115), (1010, 115), (1011, 115), (1012, 115), (1013, 115), (1014, 115), (1015, 115), (1016, 115), (1017, 115), (1018, 115), (1019, 115), (1020, 115), (1021, 115), (1022, 115), (1023, 115), (1024, 115), (1025, 115), (1026, 115), (1027, 115),
|
||||
@ -9819,7 +9821,7 @@ INSERT INTO quest (id, name) VALUES (3, 'igloo');
|
||||
|
||||
INSERT INTO quest_award_item (quest_id, item_id) VALUES (1, 24023);
|
||||
INSERT INTO quest_award_furniture (quest_id, furniture_id) VALUES (3, 2166);
|
||||
INSERT INTO quest_award_puffle_item (quest_id, puffle_item_id) VALUES (2, 146);
|
||||
INSERT INTO quest_award_puffle_item (quest_id, puffle_item_id) VALUES (2, 70);
|
||||
|
||||
INSERT INTO quest_task (quest_id, description, room_id) VALUES (1, 'Visit the Clothes Shop', 130);
|
||||
INSERT INTO quest_task (quest_id, description, room_id) VALUES (2, 'Visit the Pet Shop', 310);
|
||||
|
@ -12,6 +12,7 @@ from houdini.data.room import Room
|
||||
from houdini.data.item import Item
|
||||
from houdini.data.igloo import Igloo, Furniture, Flooring, Location
|
||||
from houdini.data.stamp import Stamp
|
||||
from houdini.data.pet import Puffle, PenguinPuffle
|
||||
|
||||
|
||||
class ChecklistError(Exception):
|
||||
@ -324,6 +325,30 @@ class StampConverter(IConverter):
|
||||
return None
|
||||
|
||||
|
||||
class PuffleConverter(IConverter):
|
||||
|
||||
description = """Converts a puffle ID into a houdini.data.Puffle instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
puffle_id = int(ctx.argument)
|
||||
try:
|
||||
return ctx.p.server.puffles[puffle_id]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class PenguinPuffleConverter(IConverter):
|
||||
|
||||
description = """Converts a penguin puffle ID into a houdini.data.PenguinPuffle instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
puffle_id = int(ctx.argument)
|
||||
try:
|
||||
return ctx.p.puffles[puffle_id]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class SeparatorConverter(IConverter):
|
||||
|
||||
__slots__ = ['separator', 'mapper']
|
||||
@ -415,7 +440,10 @@ ConverterTypes = {
|
||||
Igloo: IglooConverter,
|
||||
Flooring: FlooringConverter,
|
||||
Location: LocationConverter,
|
||||
Stamp: StampConverter
|
||||
Stamp: StampConverter,
|
||||
Puffle: PuffleConverter,
|
||||
|
||||
PenguinPuffle: PenguinPuffleConverter
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,9 +53,11 @@ class Penguin(db.Model):
|
||||
ninja_matches_won = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
fire_matches_won = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
water_matches_won = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
rainbow_adoptability = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
rainbow_adoptability = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
has_dug = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
puffle_handler = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
nuggets = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
walking = db.Column(db.ForeignKey('penguin_puffle.id', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
opened_playercard = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
special_wave = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
special_dance = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
@ -107,6 +109,9 @@ class Penguin(db.Model):
|
||||
if (self.status_field & field_bitmask) == 0:
|
||||
await self.update(status_field=self.status_field ^ field_bitmask).apply()
|
||||
|
||||
def status_field_get(self, field_bitmask):
|
||||
return (self.status_field & field_bitmask) != 0
|
||||
|
||||
@property
|
||||
def minutes_played_today(self):
|
||||
async def get_minutes():
|
||||
|
@ -7,12 +7,11 @@ class Puffle(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
parent_id = db.Column(db.ForeignKey('puffle.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
name = db.Column(db.String(50), nullable=False, server_default=db.text("''::character varying"))
|
||||
cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
favourite_food = db.Column(db.ForeignKey('puffle_item.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
favourite_toy = db.Column(db.ForeignKey('puffle_item.id', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
runaway_postcard = db.Column(db.ForeignKey('postcard.id', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
max_food = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
max_rest = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
max_clean = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
|
||||
|
||||
class PuffleItem(db.Model):
|
||||
@ -72,7 +71,6 @@ class PenguinPuffle(db.Model):
|
||||
play = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
rest = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
clean = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
walking = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
hat = db.Column(db.ForeignKey('puffle_item.id', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
backyard = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
has_dug = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
|
@ -1,35 +1,714 @@
|
||||
from houdini import handlers
|
||||
from houdini.handlers import XTPacket
|
||||
from houdini.handlers.play.navigation import handle_join_server
|
||||
from houdini.constants import ClientType
|
||||
from houdini.constants import ClientType, StatusField
|
||||
|
||||
from houdini.data.pet import PenguinPuffleCollection, PenguinPuffleItemCollection
|
||||
from houdini.data.pet import PenguinPuffleCollection, PenguinPuffleItemCollection, PenguinPuffle
|
||||
from houdini.data.room import PenguinBackyardRoom, PenguinIglooRoom
|
||||
from houdini.data.mail import PenguinPostcard
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import time
|
||||
import random
|
||||
import asyncio
|
||||
import operator
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('j', 'js'), after=handle_join_server)
|
||||
@handlers.player_attribute(joined_world=True)
|
||||
PuffleKillerInterval = 600
|
||||
LegacyPuffleIds = [0, 1, 2, 3, 4, 5, 6, 7, 8]
|
||||
|
||||
BrushCareItemId = 1
|
||||
BathCareItemId = 8
|
||||
SleepCareItemId = 37
|
||||
BasicCareInventory = [BrushCareItemId, BathCareItemId, SleepCareItemId]
|
||||
|
||||
|
||||
async def decrease_stats(server):
|
||||
while True:
|
||||
await asyncio.sleep(PuffleKillerInterval)
|
||||
for penguin in server.penguins_by_id.values():
|
||||
if type(penguin.room) != PenguinIglooRoom or penguin.room.penguin_id != penguin.id:
|
||||
for puffle_id in list(penguin.puffles.keys()):
|
||||
puffle = penguin.puffles[puffle_id]
|
||||
puffle_crumbs = server.puffles[puffle.puffle_id]
|
||||
is_legacy_puffle = penguin.is_legacy_client and puffle.puffle_id in LegacyPuffleIds
|
||||
is_vanilla_puffle = penguin.is_vanilla_client and not puffle.backyard
|
||||
if is_vanilla_puffle or is_legacy_puffle:
|
||||
if puffle.id == penguin.walking:
|
||||
await puffle.update(
|
||||
food=max(10, puffle.food - 8),
|
||||
rest=max(10, puffle.rest - 8),
|
||||
clean=max(10, puffle.clean - 8)
|
||||
).apply()
|
||||
else:
|
||||
await puffle.update(
|
||||
food=max(0, puffle.food - 4),
|
||||
play=max(0, puffle.play - 4),
|
||||
rest=max(0, puffle.rest - 4),
|
||||
clean=max(0, puffle.clean - 4)
|
||||
).apply()
|
||||
if is_legacy_puffle and puffle.food == puffle.rest == puffle.clean == 0:
|
||||
await penguin.add_inbox(server.postcards[puffle_crumbs.runaway_postcard], details=puffle.name)
|
||||
await penguin.puffles.delete(puffle.id)
|
||||
elif is_legacy_puffle and puffle.food < 10:
|
||||
notification_aware = await PenguinPostcard.query.where(
|
||||
(PenguinPostcard.penguin_id == penguin.id)
|
||||
& (PenguinPostcard.postcard_id == 110)
|
||||
& (PenguinPostcard.details == puffle.name)).gino.scalar()
|
||||
if not notification_aware:
|
||||
await penguin.add_inbox(server.postcards[110], details=puffle.name)
|
||||
|
||||
|
||||
async def dig(p, on_command=False):
|
||||
if p.walking is not None:
|
||||
treasure_types = {0: 'coins', 1: 'food', 2: 'furniture', 3: 'clothing', None: None}
|
||||
walking_puffle = p.puffles[p.walking]
|
||||
|
||||
treasure_quantity, item_id = 1, 0
|
||||
|
||||
if p.can_dig_gold:
|
||||
treasure_types = {4: 'golden', None: None}
|
||||
|
||||
puffle_age = (datetime.now() - walking_puffle.adoption_date).days
|
||||
puffle_health = walking_puffle.food + walking_puffle.play + walking_puffle.rest + walking_puffle.clean
|
||||
age_percent = puffle_age / 365
|
||||
health_percent = puffle_health / 400
|
||||
overall_percent = (age_percent + health_percent * 2) / 3
|
||||
|
||||
if overall_percent > random.random() and p.is_member:
|
||||
treasure_type_id = random.choice(list(treasure_types))
|
||||
treasure_type = treasure_types[treasure_type_id]
|
||||
else:
|
||||
treasure_type_id = random.choice([0, None])
|
||||
treasure_type = treasure_types[treasure_type_id]
|
||||
|
||||
if not on_command and treasure_type is None:
|
||||
return await p.room.send_xt('nodig', p.id, 1)
|
||||
elif treasure_type == 'food':
|
||||
diggable_food_ids = [t.puffle_item_id for t in p.server.puffle_food_treasure
|
||||
if t.puffle_id == walking_puffle.puffle_id
|
||||
and t.puffle_item_id not in p.puffle_items]
|
||||
if diggable_food_ids:
|
||||
item_id = random.choice(diggable_food_ids)
|
||||
await p.add_puffle_item(p.server.puffle_items[item_id], notify=False, cost=0)
|
||||
if item_id == p.server.puffles[walking_puffle.puffle_id].favourite_food:
|
||||
await p.add_stamp(p.server.stamps[495])
|
||||
elif treasure_type == 'furniture':
|
||||
diggable_furniture_ids = [t.furniture_id for t in p.server.puffle_furniture_treasure
|
||||
if t.puffle_id == walking_puffle.puffle_id
|
||||
and t.furniture_id not in p.furniture]
|
||||
if diggable_furniture_ids:
|
||||
item_id = random.choice(diggable_furniture_ids)
|
||||
await p.add_furniture(p.server.furniture[item_id], notify=False, cost=0)
|
||||
await p.add_stamp(p.server.stamps[494])
|
||||
elif treasure_type == 'clothing':
|
||||
diggable_clothing_ids = [t.item_id for t in p.server.puffle_clothing_treasure
|
||||
if t.puffle_id == walking_puffle.puffle_id
|
||||
and t.item_id not in p.inventory]
|
||||
if diggable_clothing_ids:
|
||||
item_id = random.choice(diggable_clothing_ids)
|
||||
await p.add_inventory(p.server.items[item_id], notify=False, cost=0)
|
||||
await p.add_stamp(p.server.stamps[494])
|
||||
elif treasure_type == 'golden':
|
||||
item_id = 1
|
||||
treasure_quantity = random.randrange(1, 4)
|
||||
|
||||
await p.update(nuggets=p.nuggets + treasure_quantity).apply()
|
||||
await p.send_xt('currencies', f'1|{p.nuggets}')
|
||||
|
||||
if not item_id:
|
||||
treasure_type_id, treasure_type = 0, 'coins'
|
||||
|
||||
if (on_command and treasure_type is None) or treasure_type == 'coins':
|
||||
treasure_quantity = random.randrange(10, 250)
|
||||
await p.update(coins=p.coins + treasure_quantity).apply()
|
||||
if treasure_quantity >= 50:
|
||||
await p.add_stamp(p.server.stamps[493])
|
||||
|
||||
if not p.has_dug:
|
||||
await p.add_stamp(p.server.stamps[489])
|
||||
for player in p.room.penguins_by_id.values():
|
||||
if player.id != p.id:
|
||||
await player.add_stamp(p.server.stamps[490])
|
||||
|
||||
await p.room.send_xt('puffledig', p.id, p.walking, treasure_type_id, item_id,
|
||||
treasure_quantity, int(not p.has_dug))
|
||||
await p.update(has_dug=True).apply()
|
||||
await walking_puffle.update(has_dug=True).apply()
|
||||
|
||||
color_dig_count = len({puffle.puffle_id for puffle in p.puffles.values() if puffle.has_dug})
|
||||
if color_dig_count >= 11:
|
||||
await p.add_stamp(p.server.stamps[491])
|
||||
|
||||
await p.server.redis.setex(f'houdini.last_dig.{p.id}', 120, int(time.time()))
|
||||
dig_count = await p.server.redis.incr(f'houdini.dig_count.{p.id}')
|
||||
|
||||
if dig_count == 1:
|
||||
await p.server.redis.expireat(f'houdini.dig_count.{p.id}',
|
||||
(datetime.now() + timedelta(days=1)).timestamp())
|
||||
if dig_count == 5:
|
||||
await p.add_stamp(p.server.stamps[492])
|
||||
|
||||
await p.status_field_set(StatusField.PuffleTreasureInfographic)
|
||||
|
||||
|
||||
async def deliver(p, care_item, puffle):
|
||||
if care_item.cost != 0 and care_item.id not in p.puffle_items:
|
||||
await p.add_puffle_item(care_item)
|
||||
|
||||
if care_item.cost == 0 or care_item.id in p.puffle_items:
|
||||
if care_item.type == 'food':
|
||||
quantity_owned = p.puffle_items[care_item.id].quantity
|
||||
if quantity_owned > 1:
|
||||
await p.puffle_items[care_item.id].update(quantity=quantity_owned - 1).apply()
|
||||
elif quantity_owned == 1:
|
||||
await p.puffle_items.delete(care_item.id)
|
||||
|
||||
if care_item.id == p.server.puffles[puffle.puffle_id].favourite_food:
|
||||
await puffle.update(food=100, play=100, rest=100, clean=100).apply()
|
||||
else:
|
||||
await puffle.update(
|
||||
food=max(0, min(puffle.food + care_item.food_effect, 100)),
|
||||
play=max(0, min(puffle.play + care_item.play_effect, 100)),
|
||||
rest=max(0, min(puffle.rest + care_item.rest_effect, 100)),
|
||||
clean=max(0, min(puffle.clean + care_item.clean_effect, 100)),
|
||||
).apply()
|
||||
|
||||
celebration = puffle.food == puffle.play == puffle.rest == puffle.clean == 100
|
||||
|
||||
care_item_delivery = f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}|{int(celebration)}'
|
||||
await p.room.send_xt('pcid', p.id, care_item_delivery)
|
||||
|
||||
if care_item.id == 126:
|
||||
p.can_dig_gold = True
|
||||
await p.room.send_xt('oberry', p.id, p.walking)
|
||||
await p.send_xt('currencies', f'1|{p.nuggets}')
|
||||
|
||||
|
||||
def get_client_puffle_id(p, puffle_id):
|
||||
parent_id = p.server.puffles[puffle_id].parent_id
|
||||
return (parent_id, puffle_id) if parent_id is not None else (puffle_id, '')
|
||||
|
||||
|
||||
def get_client_puffle_id_string(p, puffle_id):
|
||||
parent_id, puffle_id = get_client_puffle_id(p, puffle_id)
|
||||
return f'{parent_id}|{puffle_id}'
|
||||
|
||||
|
||||
def get_my_player_puffles(p):
|
||||
if p.is_vanilla_client:
|
||||
return [f'{puffle.id}|{get_client_puffle_id_string(p, puffle.puffle_id)}|'
|
||||
f'{puffle.name}|{int(time.mktime(puffle.adoption_date.timetuple()))}|{puffle.food}|{puffle.play}|'
|
||||
f'{puffle.rest}|{puffle.clean}|{puffle.hat or 0}|0' for puffle in p.puffles.values()]
|
||||
else:
|
||||
return [f'{puffle.id}|{puffle.name}|{puffle.puffle_id}|{puffle.clean}|'
|
||||
f'{puffle.food}|{puffle.rest}|100|100|100' for puffle in p.puffles.values()
|
||||
if puffle.puffle_id in LegacyPuffleIds]
|
||||
|
||||
|
||||
def get_my_player_walking_puffle(p):
|
||||
if p.walking is not None and p.is_vanilla_client:
|
||||
puffle = p.puffles[p.walking]
|
||||
parent_id, puffle_id = get_client_puffle_id(p, puffle.puffle_id)
|
||||
return f'{puffle.id}|{parent_id}|{puffle_id}|{puffle.hat or 0}|0'
|
||||
return str()
|
||||
|
||||
|
||||
def check_name(p, puffle_name):
|
||||
tokens = puffle_name.lower().split()
|
||||
clean = not any(word in tokens for word in p.server.chat_filter_words.keys())
|
||||
length_ok = 1 <= len(puffle_name) <= 12
|
||||
characters_ok = puffle_name.isalpha()
|
||||
return characters_ok and length_ok and clean
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('j', 'js'), before=handle_join_server, pre_login=True)
|
||||
@handlers.allow_once
|
||||
async def load_pet_inventory(p):
|
||||
p.puffles = await PenguinPuffleCollection.get_collection(p.id)
|
||||
p.puffle_items = await PenguinPuffleItemCollection.get_collection(p.id)
|
||||
|
||||
await p.send_xt('pgu', *get_my_player_puffles(p))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'getdigcooldown'), pre_login=True)
|
||||
async def handle_get_dig_cooldown(p):
|
||||
await p.send_xt('getdigcooldown', 1)
|
||||
last_dig = await p.server.redis.get(f'houdini.last_dig.{p.id}')
|
||||
|
||||
if last_dig is not None:
|
||||
cooldown_remaining = max(0, 120 - (int(time.time()) - int(last_dig)))
|
||||
return await p.send_xt('getdigcooldown', cooldown_remaining)
|
||||
await p.send_xt('getdigcooldown', 0)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'checkpufflename'))
|
||||
async def handle_check_puffle_name_with_response(p, puffle_name):
|
||||
name_ok = puffle_name.isalnum()
|
||||
name_ok = check_name(p, puffle_name)
|
||||
await p.send_xt('checkpufflename', puffle_name, int(name_ok))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pcn'))
|
||||
async def handle_check_puffle_name(p, puffle_name):
|
||||
name_ok = check_name(p, puffle_name)
|
||||
await p.send_xt('pcn', puffle_name, int(name_ok))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pg'), client=ClientType.Vanilla)
|
||||
async def handle_get_player_puffles(p, penguin_id: int, room_type: str):
|
||||
await p.send_xt('pg')
|
||||
async def handle_get_player_puffles_vanilla(p, penguin_id: int, room_type: str):
|
||||
is_backyard = room_type == 'backyard'
|
||||
owned_puffles = await PenguinPuffle.query.where((PenguinPuffle.penguin_id == penguin_id)
|
||||
& (PenguinPuffle.backyard == is_backyard)).gino.all()
|
||||
walking = p.server.penguins_by_id[penguin_id].walking if penguin_id in p.server.penguins_by_id else None
|
||||
|
||||
player_puffles = [f'{puffle.id}|{get_client_puffle_id_string(p, puffle.puffle_id)}|'
|
||||
f'{puffle.name}||{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}|'
|
||||
f'{puffle.hat or 0}|0|0|{int(puffle.id == walking)}' for puffle in owned_puffles]
|
||||
await p.send_xt('pg', len(owned_puffles), *player_puffles)
|
||||
if len(owned_puffles) >= 10:
|
||||
await p.status_field_set(StatusField.MoreThanTenPufflesBackyardMessage)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pg'), client=ClientType.Legacy)
|
||||
async def handle_get_player_puffles_legacy(p, penguin_id: int):
|
||||
await p.send_xt('pg')
|
||||
owned_puffles = await PenguinPuffle.query.where((PenguinPuffle.penguin_id == penguin_id)).gino.all()
|
||||
|
||||
walking = p.server.penguins_by_id[penguin_id].walking if penguin_id in p.server.penguins_by_id else None
|
||||
|
||||
player_puffles = [f'{puffle.id}|{puffle.name}|{puffle.puffle_id}|'
|
||||
f'{puffle.clean}|{puffle.food}|{puffle.rest}|100|100|100|0|0|0|{int(puffle.id == walking)}'
|
||||
for puffle in owned_puffles if puffle.puffle_id in LegacyPuffleIds]
|
||||
await p.send_xt('pg', *player_puffles)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pgu'))
|
||||
async def handle_get_my_player_puffles(p):
|
||||
await p.send_xt('pgu', *get_my_player_puffles(p))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pn'), client=ClientType.Vanilla)
|
||||
async def handle_adopt_puffle_vanilla(p, type_id: int, name: str, subtype_id: int):
|
||||
if type_id not in p.server.puffles or not check_name(p, name):
|
||||
return await p.send_error(441)
|
||||
|
||||
name = name.title()
|
||||
cost = p.server.puffles[type_id].cost
|
||||
|
||||
if p.coins < cost:
|
||||
return await p.send_error(401)
|
||||
|
||||
if len(p.puffles) >= 75:
|
||||
return await p.send_error(440)
|
||||
|
||||
puffle_id = subtype_id if bool(subtype_id) else type_id
|
||||
|
||||
if type_id == 10:
|
||||
if not p.rainbow_adoptability:
|
||||
return await p.send_error(441)
|
||||
await p.update(rainbow_adoptability=False).apply()
|
||||
elif type_id == 11:
|
||||
await p.update(nuggets=p.nuggets - 15).apply()
|
||||
p.can_dig_gold = False
|
||||
elif subtype_id == 0:
|
||||
await p.add_puffle_item(p.server.puffle_items[3], quantity=5, cost=0)
|
||||
await p.add_puffle_item(p.server.puffle_items[79], cost=0)
|
||||
await p.add_puffle_item(p.server.puffle_items[p.server.puffles[puffle_id].favourite_toy])
|
||||
|
||||
await p.update(coins=p.coins - cost).apply()
|
||||
|
||||
puffle = await p.puffles.insert(puffle_id=puffle_id, name=name)
|
||||
|
||||
parent_id, puffle_id = get_client_puffle_id(p, puffle.puffle_id)
|
||||
puffle_string = f'{puffle.id}|{parent_id}|{puffle_id}|{puffle.name}|{int(time.time())}' \
|
||||
f'|100|100|100|100|0|0'
|
||||
|
||||
await p.send_xt('pn', p.coins, puffle_string)
|
||||
|
||||
await p.add_inbox(p.server.postcards[111], details=puffle.name)
|
||||
|
||||
igloo_puffle_count = sum(not puff.backyard for puff in p.puffles.values())
|
||||
if igloo_puffle_count > 10:
|
||||
puffle_to_relocate = next(puff for puff in p.puffles.values() if not puff.backyard)
|
||||
await puffle_to_relocate.update(backyard=True).apply()
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pn'), client=ClientType.Legacy)
|
||||
async def handle_adopt_puffle_legacy(p, type_id: int, name: str):
|
||||
if type_id not in LegacyPuffleIds or not check_name(p, name):
|
||||
return await p.send_error(441)
|
||||
|
||||
name = name.title()
|
||||
cost = 800
|
||||
|
||||
if p.coins < cost:
|
||||
return await p.send_error(401)
|
||||
|
||||
if len(p.puffles) >= 18:
|
||||
return await p.send_error(440)
|
||||
|
||||
await p.update(coins=p.coins - cost).apply()
|
||||
|
||||
puffle = await p.puffles.insert(puffle_id=type_id, name=name)
|
||||
|
||||
puffle_string = f'{puffle.id}|{puffle.name}|{puffle.puffle_id}|100|100|100|100|100|100'
|
||||
await p.send_xt('pn', p.coins, puffle_string)
|
||||
|
||||
await p.add_inbox(p.server.postcards[111], details=puffle.name)
|
||||
await p.add_puffle_item(p.server.puffle_items[p.server.puffles[type_id].favourite_toy], notify=False)
|
||||
await p.send_xt('pgu', *get_my_player_puffles(p))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pgpi'), client=ClientType.Vanilla)
|
||||
async def handle_get_care_inventory(p):
|
||||
await p.send_xt('pgpi',
|
||||
*(f'{item_id}|1' for item_id in BasicCareInventory),
|
||||
*(f'{care_item.item_id}|{care_item.quantity}' for care_item in p.puffle_items.values()))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pm'))
|
||||
async def handle_puffle_move(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
await p.room.send_xt('pm', f'{puffle.id}|{x}|{y}', f=operator.attrgetter('is_vanilla_client'))
|
||||
await p.room.send_xt('pm', puffle.id, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'ps'))
|
||||
async def handle_puffle_frame(p, puffle_id: int, frame_id: int):
|
||||
if puffle_id in p.puffles:
|
||||
await p.room.send_xt('ps', puffle_id, frame_id)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pw'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_walk_vanilla(p, puffle: PenguinPuffle, walking: int):
|
||||
if not p.walking and walking:
|
||||
await p.update(walking=puffle.id).apply()
|
||||
parent_id, puffle_id = get_client_puffle_id(p, puffle.puffle_id)
|
||||
await p.room.send_xt('pw', p.id, puffle.id, parent_id, puffle_id, 1, puffle.hat or 0)
|
||||
elif not walking and puffle.id == p.walking:
|
||||
igloo_puffle_count = sum(not puff.backyard and puff.id != puffle.id for puff in p.puffles.values())
|
||||
in_backyard = type(p.room) == PenguinBackyardRoom
|
||||
return_to_backyard = in_backyard or type(p.room) != PenguinIglooRoom and puffle.backyard
|
||||
if igloo_puffle_count >= 10 and not return_to_backyard:
|
||||
return await p.send_error(443)
|
||||
|
||||
await puffle.update(backyard=return_to_backyard).apply()
|
||||
|
||||
await p.update(walking=None).apply()
|
||||
await p.room.send_xt('pw', p.id, puffle.id, 0, 0, 0, 0)
|
||||
|
||||
puffle_string = f'{puffle.id}||||||||||||{walking}'
|
||||
await p.room.send_xt('pw', p.id, puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
p.can_dig_gold = False
|
||||
|
||||
if not p.status_field_get(StatusField.HasWalkedPuffleFirstTime):
|
||||
await p.status_field_set(StatusField.HasWalkedPuffleFirstTime)
|
||||
else:
|
||||
await p.status_field_set(StatusField.HasWalkedPuffleSecondTime)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pw'), client=ClientType.Legacy)
|
||||
async def handle_puffle_walk_legacy(p, puffle: PenguinPuffle, walking: int):
|
||||
if puffle.id != p.walking and walking:
|
||||
if puffle.rest < 20 and puffle.food < 40:
|
||||
return
|
||||
|
||||
await p.update(walking=puffle.id).apply()
|
||||
await p.room.send_xt('pw', p.id, puffle.id, -1, str(), 1, 0,
|
||||
f=operator.attrgetter('is_vanilla_client'))
|
||||
elif puffle.id == p.walking and not walking:
|
||||
await p.update(walking=None).apply()
|
||||
await p.room.send_xt('pw', p.id, puffle.id, 0, 0, 0, 0,
|
||||
f=operator.attrgetter('is_vanilla_client'))
|
||||
|
||||
puffle_string = f'{puffle.id}||||||||||||{walking}'
|
||||
await p.room.send_xt('pw', p.id, puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('s', 'upa'), client=ClientType.Legacy)
|
||||
async def handle_wear_puffle(p, item_id: int):
|
||||
if p.walking:
|
||||
walking_puffle = p.puffles[p.walking]
|
||||
if item_id == walking_puffle.puffle_id + 750:
|
||||
await p.update(hand=item_id).apply()
|
||||
await p.room.send_xt('upa', p.id, item_id)
|
||||
else:
|
||||
await p.update(walking=None).apply()
|
||||
|
||||
|
||||
@handlers.disconnected
|
||||
@handlers.player_attribute(client_type=ClientType.Legacy)
|
||||
async def handle_stop_walking(p):
|
||||
if p.walking:
|
||||
await p.update(hand=None, walking=None).apply()
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pp'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_play_vanilla(p, puffle: PenguinPuffle):
|
||||
favourite_toy = p.server.puffle_items[p.server.puffles[puffle.puffle_id].favourite_toy]
|
||||
await deliver(p, favourite_toy, puffle)
|
||||
|
||||
legacy_puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
vanilla_puffle_string = f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}'
|
||||
await p.room.send_xt('pp', legacy_puffle_string, 1, f=operator.attrgetter('is_legacy_client'))
|
||||
await p.room.send_xt('pp', p.id, vanilla_puffle_string, f=operator.attrgetter('is_vanilla_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pp'), client=ClientType.Legacy)
|
||||
async def handle_puffle_play_legacy(p, puffle: PenguinPuffle):
|
||||
if puffle.rest < 20 or puffle.clean < 10:
|
||||
return
|
||||
|
||||
negative_food = random.randrange(10, 25)
|
||||
negative_rest = random.randrange(10, 25)
|
||||
await puffle.update(
|
||||
food=max(0, puffle.food - negative_food),
|
||||
rest=max(0, puffle.rest - negative_rest),
|
||||
clean=min(100, puffle.clean + 10)
|
||||
).apply()
|
||||
|
||||
play_type = 1 if puffle.rest > 80 else random.choice([0, 2])
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('pp', puffle_string, play_type, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pr'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_rest_vanilla(p, puffle: PenguinPuffle):
|
||||
sleep = p.server.puffle_items[37]
|
||||
|
||||
await deliver(p, sleep, puffle)
|
||||
|
||||
legacy_puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
vanilla_puffle_string = f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}'
|
||||
await p.room.send_xt('pr', legacy_puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
await p.room.send_xt('pr', p.id, vanilla_puffle_string, f=operator.attrgetter('is_vanilla_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pr'), client=ClientType.Legacy)
|
||||
async def handle_puffle_rest_legacy(p, puffle: PenguinPuffle):
|
||||
positive_rest = random.randrange(15, 40)
|
||||
await puffle.update(rest=min(100, puffle.rest + positive_rest)).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('pr', puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pt'), client=ClientType.Legacy)
|
||||
async def handle_puffle_treat_legacy(p, puffle: PenguinPuffle, treat_id: int):
|
||||
if p.coins > 5:
|
||||
positive_food = random.randrange(5, 15)
|
||||
await puffle.update(food=min(100, puffle.food + positive_food)).apply()
|
||||
await p.update(coins=p.coins - 5).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('pt', p.coins, puffle_string, treat_id, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pf'), client=ClientType.Legacy)
|
||||
async def handle_puffle_feed_legacy(p, puffle: PenguinPuffle):
|
||||
if p.coins > 10:
|
||||
positive_food = random.randrange(15, 40)
|
||||
await puffle.update(food=min(100, puffle.food + positive_food)).apply()
|
||||
await p.update(coins=p.coins - 10).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('pf', p.coins, puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pb'), client=ClientType.Legacy)
|
||||
async def handle_puffle_bath_legacy(p, puffle: PenguinPuffle):
|
||||
if p.coins > 5:
|
||||
additional_rest = random.randrange(5, 15)
|
||||
additional_clean = random.randrange(15, 40)
|
||||
await puffle.update(
|
||||
rest=min(100, puffle.rest + additional_rest),
|
||||
clean=min(100, puffle.clean + additional_clean)
|
||||
).apply()
|
||||
|
||||
await p.update(coins=p.coins - 5).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('pb', p.coins, puffle_string, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'ip'), client=ClientType.Legacy)
|
||||
async def handle_puffle_play_interation_legacy(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
if puffle.rest < 20 or puffle.clean < 10:
|
||||
return
|
||||
|
||||
negative_food = random.randrange(15, 25)
|
||||
negative_rest = random.randrange(15, 25)
|
||||
await puffle.update(
|
||||
food=max(0, puffle.food - negative_food),
|
||||
rest=max(0, puffle.rest - negative_rest),
|
||||
clean=min(100, puffle.clean + 20)
|
||||
).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('ip', puffle_string, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'ir'), client=ClientType.Legacy)
|
||||
async def handle_puffle_rest_interation_legacy(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
positive_rest = random.randrange(20, 50)
|
||||
await puffle.update(rest=min(100, puffle.rest + positive_rest)).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('ir', puffle_string, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'if'), client=ClientType.Legacy)
|
||||
async def handle_puffle_feed_interation_legacy(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
positive_food = random.randrange(20, 50)
|
||||
await puffle.update(
|
||||
food=min(100, puffle.food + positive_food)
|
||||
).apply()
|
||||
|
||||
await p.update(coins=p.coins - 10).apply()
|
||||
|
||||
puffle_string = f'{puffle.id}|puf|fle|{puffle.clean}|{puffle.food}|{puffle.rest}'
|
||||
await p.room.send_xt('if', p.coins, puffle_string, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'ip'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_play_interation_legacy(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
favourite_toy = p.server.puffle_items[p.server.puffles[puffle.puffle_id].favourite_toy]
|
||||
await deliver(p, favourite_toy, puffle)
|
||||
|
||||
puffle_string = f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}|{x}|{y}'
|
||||
await p.room.send_xt('ip', puffle_string)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'ir'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_rest_interation_vanilla(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
sleep = p.server.puffle_items[37]
|
||||
await deliver(p, sleep, puffle)
|
||||
|
||||
puffle_string = f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}|{x}|{y}'
|
||||
await p.room.send_xt('ir', puffle_string)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pip'))
|
||||
async def handle_puffle_init_play_interation(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
await p.room.send_xt('pip', f'{puffle.id}|{x}|{y}', f=operator.attrgetter('is_vanilla_client'))
|
||||
await p.room.send_xt('pip', puffle.id, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pir'))
|
||||
async def handle_puffle_init_rest_interaction(p, puffle: PenguinPuffle, x: int, y: int):
|
||||
await p.room.send_xt('pir', f'{puffle.id}|{x}|{y}', f=operator.attrgetter('is_vanilla_client'))
|
||||
await p.room.send_xt('pir', puffle.id, x, y, f=operator.attrgetter('is_legacy_client'))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'papi'), client=ClientType.Vanilla)
|
||||
async def handle_add_puffle_care_item(p, item_id: int):
|
||||
if item_id not in p.server.puffle_items:
|
||||
return await p.send_error(402)
|
||||
|
||||
care_item = p.server.puffle_items[item_id]
|
||||
|
||||
if care_item.cost > p.coins:
|
||||
return await p.send_error(401)
|
||||
|
||||
await p.add_puffle_item(care_item)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pgmps'), client=ClientType.Vanilla)
|
||||
async def handle_get_my_puffle_stats(p):
|
||||
puffle_stats = ','.join(f'{puffle.id}|{puffle.food}|{puffle.play}|{puffle.rest}|{puffle.clean}'
|
||||
for puffle in p.puffles.values())
|
||||
await p.room.send_xt('pgmps', puffle_stats)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pcid'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_care_item_delivered(p, puffle: PenguinPuffle, care_item_id: int):
|
||||
care_item = p.server.puffle_items[care_item_id]
|
||||
await deliver(p, care_item, puffle)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'phg'))
|
||||
async def handle_get_puffle_handler(p):
|
||||
await p.send_xt('phg', int(p.puffle_handler))
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'phs'))
|
||||
@handlers.allow_once
|
||||
async def handle_set_puffle_handler(p):
|
||||
await p.update(puffle_handler=True).apply()
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'puphi'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_visitor_hat_update(p, puffle: PenguinPuffle, hat_id: int):
|
||||
if hat_id in p.puffle_items:
|
||||
await puffle.update(hat=hat_id).apply()
|
||||
await p.room.send_xt('puphi', puffle.id, hat_id)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'pufflewalkswap'), client=ClientType.Vanilla)
|
||||
async def handle_walk_swap_puffles(p, puffle: PenguinPuffle):
|
||||
if puffle.id != p.walking:
|
||||
walking_puffle = p.puffles[p.walking]
|
||||
in_backyard = type(p.room) == PenguinBackyardRoom
|
||||
return_to_backyard = in_backyard or type(p.room) != PenguinIglooRoom and walking_puffle.backyard
|
||||
await walking_puffle.update(backyard=return_to_backyard).apply()
|
||||
|
||||
puffle = p.puffles[puffle.id]
|
||||
await p.update(walking=puffle.id).apply()
|
||||
|
||||
parent_id, puffle_id = get_client_puffle_id(p, puffle.puffle_id)
|
||||
await p.room.send_xt('pufflewalkswap', p.id, puffle.id, parent_id, puffle_id, 1, puffle.hat or 0)
|
||||
p.can_dig_gold = False
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'puffletrick'), client=ClientType.Vanilla)
|
||||
async def handle_puffle_trick(p, trick_id: int):
|
||||
if p.walking is not None:
|
||||
await p.room.send_xt('puffletrick', p.id, trick_id)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'puffleswap'), client=ClientType.Vanilla)
|
||||
async def handle_change_puffle_room(p, puffle: PenguinPuffle, room_type: str):
|
||||
to_backyard = room_type == 'backyard'
|
||||
igloo_puffle_count = sum(not puff.backyard and puff.id != p.walking for puff in p.puffles.values())
|
||||
if igloo_puffle_count >= 10 and not to_backyard:
|
||||
return await p.send_error(443)
|
||||
|
||||
await puffle.update(backyard=1 if to_backyard else 0).apply()
|
||||
await p.room.send_xt('puffleswap', puffle.id, room_type)
|
||||
await p.status_field_set(StatusField.PlayerSwapPuffle)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'prp'), client=ClientType.Vanilla)
|
||||
async def handle_return_puffle(p, puffle: PenguinPuffle):
|
||||
if p.walking == puffle.id:
|
||||
await p.update(walking=None).apply()
|
||||
|
||||
await p.puffles.delete(puffle.id)
|
||||
await p.room.send_xt('prp', puffle.id)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'carestationmenu'), client=ClientType.Vanilla)
|
||||
async def handle_care_station_menu(p):
|
||||
await p.send_xt('carestationmenu', '7|117', '119')
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'carestationmenuchoice'), client=ClientType.Vanilla)
|
||||
async def handle_care_station_menu_choice(p, item_id: int):
|
||||
await p.room.send_xt('carestationmenuchoice', p.id, item_id)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'puffledig'), client=ClientType.Vanilla)
|
||||
@handlers.cooldown(119)
|
||||
async def handle_puffle_dig(p):
|
||||
await dig(p)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'puffledigoncommand'), client=ClientType.Vanilla)
|
||||
@handlers.cooldown(119)
|
||||
async def handle_puffle_dig_on_command(p):
|
||||
await dig(p, on_command=True)
|
||||
|
||||
|
||||
@handlers.handler(XTPacket('p', 'revealgoldpuffle'))
|
||||
async def handle_reveal_gold_puffle(p):
|
||||
if p.can_dig_gold and p.nuggets >= 15:
|
||||
await p.room.send_xt('revealgoldpuffle', p.id)
|
||||
|
@ -20,7 +20,8 @@ from houdini.data.room import RoomCollection
|
||||
from houdini.data.stamp import StampCollection
|
||||
from houdini.data.ninja import CardCollection
|
||||
from houdini.data.mail import PostcardCollection
|
||||
from houdini.data.pet import PuffleCollection, PuffleItemCollection
|
||||
from houdini.data.pet import PuffleCollection, PuffleItemCollection, PuffleTreasurePuffleItem, \
|
||||
PuffleTreasureFurniture, PuffleTreasureItem
|
||||
from houdini.data.permission import PermissionCollection
|
||||
from houdini.data.buddy import CharacterCollection
|
||||
from houdini.data.moderator import ChatFilterRuleCollection
|
||||
@ -38,8 +39,8 @@ from houdini.handlers import XTListenerManager, XMLListenerManager, DummyEventLi
|
||||
from houdini.plugins import PluginManager
|
||||
from houdini.commands import CommandManager
|
||||
|
||||
from houdini.handlers.play.player import server_heartbeat
|
||||
from houdini.handlers.play.player import server_egg_timer
|
||||
from houdini.handlers.play.player import server_heartbeat, server_egg_timer
|
||||
from houdini.handlers.play.pet import decrease_stats
|
||||
|
||||
from houdini.handlers.play.music import SoundStudio
|
||||
|
||||
@ -87,12 +88,16 @@ class Houdini:
|
||||
self.postcards = None
|
||||
self.puffles = None
|
||||
self.puffle_items = None
|
||||
self.puffle_food_treasure = None
|
||||
self.puffle_furniture_treasure = None
|
||||
self.puffle_clothing_treasure = None
|
||||
self.characters = None
|
||||
|
||||
self.spawn_rooms = None
|
||||
|
||||
self.heartbeat = None
|
||||
self.egg_timer = None
|
||||
self.puffle_killer = None
|
||||
|
||||
self.music = None
|
||||
|
||||
@ -210,6 +215,10 @@ class Houdini:
|
||||
self.puffle_items = await PuffleItemCollection.get_collection()
|
||||
self.logger.info(f'Loaded {len(self.puffle_items)} puffle care items')
|
||||
|
||||
self.puffle_food_treasure = await PuffleTreasurePuffleItem.query.gino.all()
|
||||
self.puffle_furniture_treasure = await PuffleTreasureFurniture.query.gino.all()
|
||||
self.puffle_clothing_treasure = await PuffleTreasureItem.query.gino.all()
|
||||
|
||||
self.characters = await CharacterCollection.get_collection()
|
||||
self.logger.info(f'Loaded {len(self.characters)} characters')
|
||||
|
||||
@ -228,6 +237,7 @@ class Houdini:
|
||||
|
||||
self.heartbeat = asyncio.create_task(server_heartbeat(self))
|
||||
self.egg_timer = asyncio.create_task(server_egg_timer(self))
|
||||
self.puffle_killer = asyncio.create_task(decrease_stats(self))
|
||||
|
||||
self.music = SoundStudio(self)
|
||||
|
||||
|
@ -2,6 +2,8 @@ import time
|
||||
|
||||
from houdini.spheniscidae import Spheniscidae
|
||||
from houdini.data import penguin
|
||||
from houdini.data.mail import PenguinPostcard
|
||||
from houdini.handlers.play.pet import get_my_player_walking_puffle
|
||||
|
||||
|
||||
class Penguin(Spheniscidae, penguin.Penguin):
|
||||
@ -30,7 +32,6 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
||||
self.membership_days_remain = -1
|
||||
|
||||
self.avatar = None
|
||||
self.walking_puffle = None
|
||||
self.active_quests = None
|
||||
self.legacy_buddy_requests = set()
|
||||
|
||||
@ -39,13 +40,15 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
||||
self.login_timestamp = None
|
||||
self.egg_timer_minutes = None
|
||||
|
||||
self.can_dig_gold = False
|
||||
|
||||
@property
|
||||
def party_state(self):
|
||||
return str()
|
||||
|
||||
@property
|
||||
def puffle_state(self):
|
||||
return str()
|
||||
return get_my_player_walking_puffle(self)
|
||||
|
||||
@property
|
||||
def penguin_state(self):
|
||||
@ -120,21 +123,32 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
||||
|
||||
return True
|
||||
|
||||
async def add_puffle_item(self, care_item, quantity=1, notify=True):
|
||||
if care_item.id in self.puffle_items:
|
||||
penguin_care_item = self.puffle_items[care_item.id]
|
||||
async def add_puffle_item(self, care_item, quantity=1, notify=True, cost=None):
|
||||
if care_item.type not in ['food', 'head', 'play']:
|
||||
return False
|
||||
|
||||
care_item_id = care_item.parent_id
|
||||
quantity = quantity * care_item.quantity
|
||||
|
||||
if care_item.type == 'play' and care_item_id in self.puffle_items:
|
||||
return False
|
||||
|
||||
if care_item_id in self.puffle_items:
|
||||
penguin_care_item = self.puffle_items[care_item_id]
|
||||
if penguin_care_item.quantity >= 100:
|
||||
return False
|
||||
|
||||
await penguin_care_item.update(
|
||||
quantity=penguin_care_item.quantity + quantity).apply()
|
||||
else:
|
||||
await self.puffle_items.insert(item_id=care_item.id)
|
||||
penguin_care_item = await self.puffle_items.insert(item_id=care_item_id,
|
||||
quantity=quantity)
|
||||
|
||||
await self.update(coins=self.coins - care_item.cost).apply()
|
||||
cost = cost if cost is not None else care_item.cost
|
||||
await self.update(coins=self.coins - cost).apply()
|
||||
|
||||
if notify:
|
||||
await self.send_xt('papi', self.coins, care_item.id, quantity)
|
||||
await self.send_xt('papi', self.coins, care_item_id, penguin_care_item.quantity)
|
||||
|
||||
self.logger.info(f'{self.username} added \'{care_item.name}\' to their puffle care inventory')
|
||||
|
||||
@ -221,8 +235,8 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
||||
return True
|
||||
|
||||
async def add_inbox(self, postcard, sender_name="sys", sender_id=None, details=""):
|
||||
penguin_postcard = await self.postcards.insert(penguin_id=self.id, sender_id=sender_id,
|
||||
postcard_id=postcard.id, details=details)
|
||||
penguin_postcard = await PenguinPostcard.create(penguin_id=self.id, sender_id=sender_id,
|
||||
postcard_id=postcard.id, details=details)
|
||||
|
||||
await self.send_xt('mr', sender_name, 0, postcard.id, details, int(time.time()), penguin_postcard.id)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user