Page MenuHomePhabricator (Chris)

No OneTemporary

Size
945 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/data/labels.txt b/data/labels.txt
index e02a899..af8737f 100644
--- a/data/labels.txt
+++ b/data/labels.txt
@@ -1,2852 +1,2871 @@
[english]
start_new_game#Start a new game
start_desc#Old game will be destroyed
restore#Restore game
at#at
level#level
begin_journey#Begin your journey in a new dungeon
config_keys#Configure keys
redef_input#Redefine player's input
exit_game#Exit game
return_to_desktop#Return to the desktop
key_configuration#Keys' configuration
key_configuration_desc#Please configure the keys
key_configuration_insert#[insert Key]
key_configuration_done#DONE
key_move_up#move up
key_move_down#move down
key_move_left#move left
key_move_right#move right
key_fire_up#fire up
key_fire_down#fire down
key_fire_left#fire left
key_fire_right#fire right
key_fire_select#fire select
key_time#time control
key_fire#fire (one button)
key_spell#cast a spell
configure_game#Configuration
configure_game_desc#Changes game options
config_back#Back
config_back_desc#Back to the main menu
config_lang#Language
config_lang_desc#Choose with left and right arrows
english#English
french#French
keys_move#move
keys_fire#fire
keys_select_1#fire
keys_select_2#selector
keys_time#time control
tuto_reset#Tutorial reset
tuto_reset_desc#Resets the tutorial
menu_continue#Continue
menu_continue_desc#Continue the game
menu_save_quit#Save and quit
menu_save_quit_desc#Save and return to main screen
menu_quit#Quit without saving
menu_quit_desc#Return to main screen without saving
play_again#Press [ENTER] to play again / [ESC] to go back to the menu
items_found#Items
monsters_killed#Monsters
congratulations_1#CONGRATULATIONS!
congratulations_2#You've challenged this demo and kill the bosses!
congratulations_3#See you soon for new adventures!
by#by
critical#CRITICAL
poison#POISON
merchant_speech_0#Best price in the entire dungeon!
merchant_speech_1#Welcome to the Pawn'n'Witches shop!
merchant_speech_2#Have a look at our merchandise!
enemy_butcher#Pigman Butcher
enemy_giant_slime#Giant Slime
enemy_rat_king#Wererat King
enemy_giant_spider#Giant Spider
item_copper#Copper coin
item_copper_desc#A copper coin (value 1)
item_silver#Silver coin
item_silver_desc#A silver coin (value 5)
item_gold#Gold coin
item_gold_desc#A gold coin (value 10)
item_titan_heart#Titan's Heart
item_titan_heart_desc#Increases Max HP
item_elven_cookie#Elven Cookie
item_elven_cookie_desc#Restores and increases Max HP
item_apple#Apple
item_apple_desc#Restores 3 HP
item_apple_green#Green apple
item_apple_green_desc#Restores 3 HP and cures poison
item_bread#Bread
item_bread_desc#Restores 7 HP
item_cheese#Cheese
item_cheese_desc#Restores 15 HP
item_boss_key#Boss Key
item_boss_key_desc#Opens the Boss gate
item_sorcerer_hat#Sorcerer's Hat
item_sorcerer_hat_desc#Increases fire rate
item_velvet_boots#Velvet Boots
item_velvet_boots_desc#Increases speed
item_spell_dual#Spell : Dual bolts
item_spell_dual_desc#Shoots two bolts
item_displacement_gloves#Displacement Gloves
item_displacement_gloves_desc#Player's velocity impacts bolt's one
item_staff_desc#Increases bolt's speed and damage
item_fairy#Fairy Lilly
item_fairy_desc#Helps you in the dungeon
item_leather_belt#Leather belt
item_leather_belt_desc#Increases fire rate
item_blood_snake#Blood Snake
item_blood_snake_desc#Increases damage
item_gem_ice#Ice Gem
item_gem_ice_desc#Ice attack (freeze)
item_gem_illusion#illusion Gem
item_gem_illusion_desc#illusion attack (ignores walls and armor)
item_gem_stone#Stone Gem
item_gem_stone_desc#Stone attack (repulse)
item_gem_lightning#Lightning Gem
item_gem_lightning_desc#Lightning attack (bouncing)
item_fairy_ice#Fairy Natasha
item_fairy_ice_desc#Helps you in the dungeon
item_ring_ice#Sapphire Ring
item_ring_ice_desc#Increases ice power
item_ring_stone_desc#Increases stone power
item_ring_lightning_desc#Increases lightning power
item_ring_illusion_desc#Increases illusion power
item_spell_triple#Spell : Triple bolts
item_spell_triple_desc#Shoots three bolts
item_fairy_fire#Fairy Alicia
item_fairy_fire_desc#Helps you in the dungeon
item_fairy_target#Fairy Scarlett
item_fairy_target_desc#Helps you in the dungeon
item_robe_magician#Magician's Robe
item_robe_magician_desc#Increases armor
item_gem_fire#Fire Gem
item_gem_fire_desc#Fire attack (more damages)
item_ring_fire#Ruby Ring
item_ring_fire_desc#Increases fire power
item_manual_staff#Staff's Manual
item_manual_staff_desc#Increases fire range
item_spell_teleport#Spell: Teleport
item_spell_teleport_desc#Cast to teleport within the room
item_spell_slime_explode#Spell: Invoke Slime
item_spell_slime_explode_desc#Cast to invoke an explosive slime
item_spell_fireball#Spell: Fireball
item_spell_fireball_desc#Cast to launch a fireball
item_spell_freeze#Spell: Freeze monsters
item_spell_freeze_desc#Cast to freeze monsters
item_spell_earthquake#Spell: Earthquake
item_spell_earthquake_desc#Cast to make the dungeon shake
item_spell_protection#Spell: Magic shield
item_spell_protection_desc#Cast to summon a magic shield
item_manual_slime#Manual of Slime Mastery
item_manual_slime_desc#Slimes don't aim at you anymore
item_spell_dual_quick#Spellbook of Quick Dual bolts
item_spell_dual_quick_desc#Shoots two bolts (quick)
item_spell_triple_quick#Spellbook of Quick Triple bolts
item_spell_triple_quick_desc#Shoots three bolts (quick)
item_manual_health#Health's Manual
item_manual_health_desc#Health items give more HP
item_rear_shot#Staff cap
item_rear_shot_desc#Shots a rear bolt
item_book_random_shot#Spellbook of Random Shot
item_book_random_shot_desc#Shots a bolt in a random direction
item_spell_web#Spell: Spider Web
item_spell_web_desc#Cast to throw spider webs
item_book_magic_i#Book of magic - Tome I
item_book_magic_i_desc#Decreases spell's cooldown
item_book_magic_ii#Book of magic - Tome II
item_book_magic_ii_desc#Increases spell's power
item_sulfur#Sulfur
item_sulfur_desc#Can make corpses explode
spell_teleport#teleport
spell_slime_explode#invoke slime
spell_fireball#fireball
spell_freeze#freeze monsters
spell_earthquake#earthquake
spell_protection#magic shield
spell_web#spider web
msg_level1_0#Your quest begins!
msg_level1_1#"Witches have abducted the Archmage's daughter and brought her in their lair.
msg_level1_2#I have to recover her!"
msg_level2_0#The Archmage's daughter
msg_level2_1#"I'm coming right now to rescue you!
msg_level2_2#Hopefully, she's safe and sound..."
msg_level3_0#The Archmage's daughter
msg_level3_1#"Did I ever tell you?
msg_level3_2#The Archmage's daughter is the sweetest girl of the Magic Academy!"
msg_level4_0#The Archmage
msg_level4_1#"The Archmage is a very wise man, and also my mentor.
msg_level4_2#He's taught me we live in the best of the worlds."
msg_level5_0#The Archmage's daughter
msg_level5_1#"I remember the Archmage's daughter and me, back in the Magic Academy.
msg_level5_2#We used to play at the healer mage, hi hi!"
msg_level6_0#The Archmage
msg_level6_1#"My parents died when I was a baby.
msg_level6_2#The Archmage has raised me like his own son."
msg_rats_bats_0#Monsters
msg_rats_bats_1#"Hey! Why do we always have to exterminate rats or bats
msg_rats_bats_2#in these Rogue games ?"
msg_snakes_0#Snakes
msg_snakes_1#"I HATE snakes!"
msg_witches_0#Witches
msg_witches_1#"B... Witches!!!"
msg_gold_0#Gold
msg_gold_1#"♫ Money money money ♫
msg_gold_2#I wonder if I can buy useful items in this dungeon..."
msg_familiar_0#Fairies
msg_familiar_1#"Oh! I've got company!
msg_familiar_2#I hope I'll find others allies in the dungeon."
msg_butcher_0#The Pigman Butcher
msg_butcher_1#"Wow this one is a big guy!
msg_butcher_2#I hope I won't end up in sausages..."
msg_giant_slime_0#The Giant Slime
msg_giant_slime_1#"I am green, huge and I stink.
msg_giant_slime_2#What I am ?"
msg_cyclops_1#"How did it go in the room?
msg_cyclops_2#It's bigger and larger than the gate!"
msg_wererat_0#The Wererat
msg_wererat_1#"Looks like my old aunt..."
msg_giant_spider_before_0#The empty room
msg_giant_spider_before_1#"Is there anybody in there? "
msg_giant_spider_after_0#The Giant Spider
msg_giant_spider_after_1#"I HATE spiders"
msg_tuto_intro_0#Tutorial: Basics
msg_tuto_intro_1#Use WASD (standard) to move around the dungeon.
msg_tuto_intro_2#Fire with the arrows (standard).
msg_tuto_items_0#Tutorial: Items
msg_tuto_items_1#Items enhance your character or give you new powers.
msg_tuto_items_2#Find or buy as many as you can.
msg_tuto_heal_0#Tutorial: Health
msg_tuto_heal_1#You've lost some hit points.
msg_tuto_heal_2#Find something to heal you before you die.
msg_tuto_shops_0#Tutorial: Shops
msg_tuto_shops_1#You can find many shops in the dungeon.
msg_tuto_shops_2#Here can you spend money to buy items or food.
msg_tuto_boss_door_0#Tutorial: Boss gate
msg_tuto_boss_door_1#From here, you can see a closed golden door.
msg_tuto_boss_door_2#Here is the boss of the floor, but you need the golden key to enter.
msg_tuto_challenge_door_0#Tutorial: Challenge room
msg_tuto_challenge_door_1#Look at the decorative door. It is a challenge room.
msg_tuto_challenge_door_2#You can obtain valuable rewards here, but enter with caution!
msg_tuto_shots_0#Tutorial: Shot types
msg_tuto_shots_1#You've found a new shot type.
msg_tuto_shots_2#Press [fire select] (standard: tab) to switch between your shot types.
msg_tuto_spell_0#Tutorial: Spells
msg_tuto_spell_1#You've found a magic spell.
msg_tuto_spell_2#Press the spell key (standard: space) to cast the spell.
msg_francky_0#The... Thing
msg_francky_1#Yuk... It looks like made with various body parts!
msg_tuto_tips_0#Tutorial: Tips
msg_tuto_tips_1#Use Time Control (standard: RShift) to travel faster.
msg_tuto_tips_2#Press [Enter] to close the message box immediately.
enemy_type_bat#a bat
enemy_type_rat#a rat
enemy_type_rat_black#a black rat
enemy_type_rat_helmet#a rat with helmet
enemy_type_rat_black_helmet#a black rat with helmet
enemy_type_evil_flower#an evil flower
enemy_type_evil_flower_ice#an evil edelweiss
enemy_type_snake#a snake
enemy_type_snake_blood#a blood snake
enemy_type_slime#a slime
enemy_type_slime_red#a red slime
enemy_type_slime_blue#a blue slime
enemy_type_slime_violet#a violet slime
enemy_type_imp_blue#a blue imp
enemy_type_imp_red#a red imp
enemy_type_pumpkin#a mad pumpkin
enemy_type_witch#a witch
enemy_type_witch_red#a red witch
enemy_type_cauldron#a cauldron
enemy_type_bubble#a bubble
enemy_type_boss_butcher#the Pigman Butcher
enemy_type_boss_slime_giant#the Giant Slime
enemy_type_boss_rat_king#the Wererat King
enemy_type_boss_spider_giant#the Giant Spider
enemy_type_green_rat#a green rat
enemy_type_rock_falling#a falling rock
enemy_type_rock_missile#a throwing rock
enemy_type_spider_egg#a falling spider egg
enemy_type_spider_little#a spider
enemy_type_spider_web#a spider web
enemy_type_ghost#a ghost
enemy_type_zombie#a zombie
enemy_type_zombie_dark#a dark zombie
enemy_type_francky#the Thing
enemy_type_himself#himself
source_explosion#explosion
source_poison#poison
enemy_francky#The Thing
enemy_francky_head#The Thing's head
inventory#Inventory
dc_certificate#Death certificate
dc_killed_by#Killed by
dc_died_level#Died on level
dc_after#after
dc_minutes#min. of game
dc_killed_monsters#Slayed monsters
dc_gold#Gold
dc_challenges#Completed challenges
player_name#Player name
player_name_desc#Modify player name
hi_scores#Hall of fame
hi_scores_desc#Best scores
credits#Credits
credits_desc#Credits screen
volume_sound#Sound volume
volume_sound_desc#Use left/right arrows to change volume
volume_music#Music volume
certificate_capture#Press [F1] to save the certificate as a PNG file
certificate_saved#Saved to
msg_tuto_temple_0#Tutorial: Temple
msg_tuto_temple_1#The temple is the place where you can worship a divinity or donate.
msg_tuto_temple_2#Divinities can help you and provide bonuses.
key_interact#interact
interact_worship#Worship
interact_shop#Buy
interact_donate#Donate 10 pieces of gold
interact_donate_fail#(not enough gold)
worshipping#You're now a worshipper of
divinity_healer_0#Pomonos the Righteous
divinity_healer_1#Guardian of the righteousness and patroness of the weak.
divinity_healer_2#He appreciates donations, suffering and fighting evil.
divinity_fighter_0#Tello the Powerful
divinity_fighter_1#Lord of the fighters and the war.
divinity_fighter_2#He appreciates monsters and titans falling in battle.
divinity_ice_0#Mister Snow
divinity_ice_1#Divinity of the cold.
divinity_ice_2#He likes the use of ice.
divinity_stone_0#Gaya
divinity_stone_1#Divinity of the stone and earth.
divinity_stone_2#He likes the use of stone.
divinity_intervention_0#Divine intervention
divinity_intervention_1#intervenes in your favour.
divinity_gift_0#Divine gift
divinity_gift_1#grants you a gift.
item_floor_map#Dungeon Map
item_floor_map_desc#Reveals the map of the present floor
item_alcohol#Absinthe
item_alcohol_desc#Temporarily increases damage
item_staff#Ebony Staff
item_ring_stone#Quartz Ring
item_ring_lightning#Amethyst Ring
item_ring_illusion#Obsidian Ring
enemy_cyclops#Stone Cyclops
msg_cyclops_0#The Stone Cyclops
enemy_type_boss_cyclops#the Stone Cyclops
achiev_rats#Rodenticide
achiev_rats_desc#Kill 250 rats
achiev_100#Stingy
achiev_100_desc#Collect 100 gold coins
achiev_piety_max#Faithful
achiev_piety_max_desc#Reach the max piety level
achiev_4_hits#Brochette
achiev_4_hits_desc#Hurt 4 monsters with the same shot
achiev_complete_set#Dressed
achiev_complete_set_desc#Equip robe + hat + boots + gloves
achiev_giant_slime#Giant Slime Slayer
achiev_giant_slime_desc#Kill the Giant Slime
achiev_cyclops#Cyclops Slayer
achiev_cyclops_desc#Kill the Stone Cyclops
achiev_rat_king#Wererat Slayer
achiev_rat_king_desc#Kill the Wererat king
achiev_giant_spider#Giant Spider Slayer
achiev_giant_spider_desc#Kill the Giant Spider
achiev_francky#Thing Slayer
achiev_francky_desc#Kill the Thing
achiev_butcher#Butcher Slayer
achiev_butcher_desc#Kill the Butcher
achiev_witches#Inquisitor
achiev_witches_desc#Kill 50 witches
achiev_eggs#Bad omelette
achiev_eggs_desc#Destroy 100 eggs
achiev_vampire#Vampire Hunter
achiev_vampire_desc#Kill the Vampire
achiev_challenges#Gladiator
achiev_challenges_desc#Complete 4 challenges
achiev_win#Live a dream
achiev_win_desc#Finish the demo
func_achiev_screen#Achievements screen
func_achiev_all#All achievements descriptions
func_double_spell#Two spells drop
enemy_type_evil_flower_fire#an evil fire flower
enemy_type_bat_skeleton#a skeleton bat
enemy_type_slime_large#a large slime
enemy_type_slime_blue_large#a large blue slime
enemy_type_slime_red_large#a large red slime
enemy_type_slime_violet_large#a large violet slime
enemy_type_bogeyman#a bogeyman
enemy_type_cauldron_elemental#an elemental cauldron
menu_achievements#Achievements
menu_achievements_desc#Display the achievements
achievement_complete#Achievement unlocked!
item_spell_flower#Spell: Invoke magic flower
item_spell_flower_desc#Cast to invoke a magic flower
item_rapid_shots#Spellbook of Rapid Fire
item_rapid_shots_desc#Wizard machine gun
item_fairy_poison#Fairy Vicky
item_fairy_poison_desc#Helps you in the dungeon
item_luck#Four-leaf clover
item_luck_desc#Temporarily increases gold drop
item_fairy_powder#Fairy Dust
item_fairy_powder_desc#Temporarily excites the fairies
item_ring_poison#Emerald ring
item_ring_poison_desc#Increases poison power
item_gem_poison#Poison Gem
item_gem_poison_desc#Poison attack
item_book_prayer_i#Book of prayer - Tome I
item_book_prayer_i_desc#Increases piety
item_book_prayer_ii#Book of prayer - Tome II
item_book_prayer_ii_desc#Reduces piety cost
item_pet_slime#Slime Pet
item_pet_slime_desc#It will follow and help you
item_hat_advanced#Warlock's Hat
item_hat_advanced_desc#Elemental resistance + firerate up
item_robe_advanced#Magic robe of shock
item_robe_advanced_desc#Repulses and increases armor
item_critical#Fake mage beard
item_critical_desc#Chance to critical shot (x2)
item_critical_advanced#Fake Archmage beard
item_critical_advanced_desc#Increases critical damage (x3)
item_spell_fairy#Spell: Fairy transmutation
item_spell_fairy_desc#Cast to transmute to a fairy
item_gloves_advanced#Ranger gloves
item_gloves_advanced_desc#Disarm traps and firerate up
item_boots_advanced#Ranger boots
item_boots_advanced_desc#Increases speed
divinity_leveling_0#Devotion level up!
div_fighter_lvl#increases your damage
div_healer_lvl_2#gives you regeneration
div_healer_lvl_3#increases your regeneration
div_ice_lvl_3#increases your ice resistance
div_ice_lvl_4#increases your ice attack
div_ice_lvl_5#increases your frozen immunity
div_stone_lvl_3#increases your stone resistance
div_stone_lvl_4#increases your stone attack
div_stone_lvl_5#increases your repulse immunity
enemy_vampire#Vampire
enemy_type_vampire#the Vampire
msg_tuto_achiev_0#Tutorial: Achievements
msg_tuto_achiev_1#You've completed an achievement.
msg_tuto_achiev_2#They unlock new items or bonus.
msg_vampire_0#The Vampire
msg_vampire_1#I forgot to bring some garlic!
msg_level7_0#The Archmage's daughter
msg_level7_1#"I'm getting closer and closer.
msg_level7_2#She can't be so far from here."
divinity_air_0#Ostram
divinity_air_1#Divinity of air and storm.
divinity_air_2#He likes the use of lightning.
div_air_lvl#increases your speed
div_air_lvl_4#increases your lightning attack
config_joystick#Configure joystick
redef_joystick#Redefine joystick control
joystick_configuration#Joystick configuration
joystick_configuration_desc#Please configure the joystick
joystick_configuration_insert#[press button]
joystick_not_found#Joystick not connected!
item_scroll_revelation#Scroll of revelation
item_scroll_revelation_desc#Reveal secrets and potion effects
item_potion_01#Red potion
item_potion_02#Green potion
item_potion_03#Golden potion
item_potion_04#Blue potion
item_potion_05#Violet potion
item_potion_06#Rosa potion
item_potion_07#Orange potion
item_potion_08#Turquoise potion
item_potion_09#Brown potion
item_potion_10#Dark potion
item_potion_desc#Unknown effect
item_potion_health#Healing potion
item_potion_health_desc#Restores 18 HP
item_potion_poison#Poison potion
item_potion_poison_desc#Makes poisoned
item_potion_speed#Speed potion
item_potion_speed_desc#Temporary increases speed
item_potion_slow#Slow potion
item_potion_slow_desc#Temporary decreases speed
item_potion_weakness#Weakness potion
item_potion_weakness_desc#Temporary decreases damage
item_potion_strength#Strength potion
item_potion_strength_desc#Temporary increases damage
item_potion_oblivion#Oblivion potion
item_potion_oblivion_desc#Forget potion effects and map
item_potion_confusion#Confusion potion
item_potion_confusion_desc#Makes confused
item_potion_cure#Tabula Rasa potion
item_potion_cure_desc#Cancel all player effects
item_potion_rage#Rage potion
item_potion_rage_desc#Throws fireballs around the player
item_bag#Bag
item_bag_desc#Provides more space for consumables
msg_potion_id_0#Potion identification
msg_potion_id_1#You've identified:
msg_tuto_consumables_0#Tutorial: Consumables
msg_tuto_consumables_1#[num1-num4] to use the consumable item.
msg_tuto_consumables_2#[left shift][num1-num4] to drop it.
msg_tuto_potions_0#Tutorial: Potions
msg_tuto_potions_1#You have found a potion.
msg_tuto_potions_2#You have to drink or identify it to know its effect.
secret_found#Secret room found!
enemy_type_sausage#a sausage
enemy_type_spider_tarantula#a tarantula
effect_speed#Speed up!
effect_slow#Speed down!
effect_strength#Damage up!
effect_weakness#Damage down!
effect_confusion#Confused!
effect_forget#You forget!
effect_cure#Tabula rasa!
effect_rage#Raaaage!
item_book_alchemy#Book of alchemy
item_book_alchemy_desc#Reveals the effect of some potions
item_fairy_stone#Fairy Cassandra
item_fairy_stone_desc#Helps you in the dungeon
item_merchant#Merchant guild seal
item_merchant_desc#Better prices in shop
item_spell_time#Spell: Time stop
item_spell_time_desc#Cast to slow down the time
item_side_shots#Spellbook of side Shots
item_side_shots_desc#Shots side bolts
item_spell_lightning#Spell: Lightning
item_spell_lightning_desc#Cast to invoke a lightning
item_belt_advanced#Black belt
item_belt_advanced_desc#Increases fairies fire rate
item_rear_shot_advanced#Silver staff cap
item_rear_shot_advanced_desc#Shots two rear bolts
item_amulet_retaliation#Amulet of vengeance
item_amulet_retaliation_desc#Retaliates
item_rage_amulet#Rage Amulet
item_rage_amulet_desc#Sends fire bolts
achiev_fairies#Charlie's Angels
achiev_fairies_desc#Get 3 fairies
achiev_ghost#Ghostbuster
achiev_ghost_desc#Kill 50 ghosts
achiev_noob#True noob
achiev_noob_desc#Die in level 1
achiev_no_damage#Tough guy
achiev_no_damage_desc#Complete 1 level without losing life
achiev_no_damage2#Very tough Guy
achiev_no_damage2_desc#Complete 2 levels without losing life
achiev_no_damage3#Very very tough Guy
achiev_no_damage3_desc#Complete 3 levels without losing life
achiev_apostate#Apostate
achiev_apostate_desc#Reject a level 4 divinity
achiev_fanatic#Fanatic
achiev_fanatic_desc#Give 100 gold to temple
func_nothing#Nothing!
state_frozen#Frozen
state_slow#Slow
state_speed#Hasted
state_poison#Poisoned
state_weakness#Weakness
state_strength#Strength
state_rage#Rage
state_confusion#Confused
state_time#Time stop
state_div_protection#Protected (div)
state_div_speed#Hasted (div)
state_div_firerate#Firerate+ (div)
state_div_damage#Damage+ (div)
dc_victory#Victory certificate
dc_after_victory#Victory after
-
player#player
players#players
ui_base_damage#Base damage
ui_fire_rate#Fire rate
ui_second#second
ui_temple_donation#Temple donation
+
+divinity_illusion_0#Moryo
+divinity_illusion_1#Lord of illusion.
+divinity_illusion_2#He likes secrets and illusions.
+div_illusion_lvl_3#makes the ghosts visible
+div_illusion_lvl_4#increases your illusion attack
+
+divinity_fire_0#Magma
+divinity_fire_1#Divinity of the fire.
+divinity_fire_2#He likes the use of fire.
+div_fire_lvl_3#increases your fire resistance
+div_fire_lvl_4#increases your fire attack
+
+divinity_death_0#King Ssssrah
+divinity_death_1#Monarch of death.
+divinity_death_2#He likes death and poison.
+div_death_lvl_3#increases your poison resistance
+div_death_lvl_4#increases your poison attack
+div_death_lvl_5#increases your poison resistance
+
[french]
start_new_game#Nouvelle partie
start_desc#L'ancienne partie sera perdue
restore#Continuer la partie
at#à
level#niveau
begin_journey#Démarrer dans un nouveau donjon
config_keys#Configurer le clavier
redef_input#Redéfinir les touches
exit_game#Quitter
return_to_desktop#Retour au bureau
key_configuration#Configuration des touches
key_configuration_desc#Veuillez configurer les touches
key_configuration_insert#[presser touche]
key_configuration_done#FAIT
key_move_up#Déplacement haut
key_move_down#Déplacement bas
key_move_left#Déplacement gauche
key_move_right#Déplacement droite
key_fire_up#Tir haut
key_fire_down#Tir bas
key_fire_left#Tir gauche
key_fire_right#Tir droite
key_fire_select#Sélecteur de tir
key_time#Accélérer le temps
key_fire#Tir (un bouton)
key_spell#Lancer un sort
configure_game#Configuration
configure_game_desc#Modifier les options
config_back#Retour
config_back_desc#Retour au menu principal
config_lang#Langue
config_lang_desc#Choisissez avec les flèches (gauche et droite)
english#Anglais
french#Français
keys_move#déplacer
keys_fire#tirer
keys_select_1#sélecteur
keys_select_2#de tir
keys_time#accélerer
tuto_reset#RAZ des tutoriels
tuto_reset_desc#Réinitialisation des tutoriels
menu_continue#Continuer
menu_continue_desc#Continuer le jeu
menu_save_quit#Sauvegarder et quitter
menu_save_quit_desc#Sauvegarder et retourner au menu principal
menu_quit#Quitter sans sauvegarder
menu_quit_desc#Retourner au menu principal sans sauvegarder
inventory#Equipement
play_again#Pressez [ENTRER] pour rejouer / [ESC] pour revenir au menu
items_found#Objets
monsters_killed#Monstres
congratulations_1#FELICITATIONS !
congratulations_2#Vous êtes brillamment arrivé au terme de cette démo !
congratulations_3#A bientôt pour de nouvelles aventures !
by#par
critical#CRITIQUE
poison#POISON
merchant_speech_0#Les meilleures affaires du donjon !
merchant_speech_1#Bienvenue à la boutique Pawn'n'Witches !
merchant_speech_2#Jetez un oeil à nos produits !
enemy_butcher#Boucher porcin
enemy_giant_slime#Slime géant
enemy_cyclops#Cyclope de pierre
enemy_rat_king#Roi des Rats-garous
enemy_giant_spider#Araignée géante
enemy_francky#La Chose
enemy_francky_head#La tête de la Chose
item_copper#Pièce de cuivre
item_copper_desc#Une pièce de cuivre (1)
item_silver#Pièce d'argent
item_silver_desc#Une pièce d'argent (5)
item_gold#Pièce d'or
item_gold_desc#Une pièce d'or (10)
item_titan_heart#Coeur de Titan
item_titan_heart_desc#Augmente les PV max
item_elven_cookie#Biscuit elfique
item_elven_cookie_desc#Guérit et augmente les PV
item_apple#Pomme
item_apple_desc#Guérit 3 PV
item_apple_green#Pomme verte
item_apple_green_desc#Guérit le poison et 3 PV
item_bread#Pain
item_bread_desc#Guérit 7 HP
item_cheese#Fromage
item_cheese_desc#Guérit 15 HP
item_boss_key#Clef du boss
item_boss_key_desc#Ouvre la porte du boss
item_sorcerer_hat#Chapeau d'enchanteur
item_sorcerer_hat_desc#Augmente la cadence de tir
item_velvet_boots#Bottes de velours
item_velvet_boots_desc#Augmente la vitesse
item_spell_dual#Sort: double-tir
item_spell_dual_desc#Tire deux projectiles
item_rage_amulet#Amulette de rage
item_rage_amulet_desc#Tire régulièrement des boules de feu
item_displacement_gloves#Gants de déplacement
item_displacement_gloves_desc#La vitesse du porteur impacte la trajectoire
item_staff#Bâton d'ébène
item_staff_desc#Augmente les dégâts et la vitesse de tir
item_fairy#La fée Lilly
item_fairy_desc#Vous aide dans le donjon
item_leather_belt#Ceinture de cuir
item_leather_belt_desc#Augmente la cadence de tir
item_blood_snake#Serpent de sang
item_blood_snake_desc#Augmente les dégâts
item_gem_ice#Gemme de glace
item_gem_ice_desc#Attaque de glace (gèle)
item_gem_illusion#Gemme d'illusion
item_gem_illusion_desc#Attaque d'illusion(ignore murs et armures)
item_gem_stone#Gemme de pierre
item_gem_stone_desc#Attaque de pierre(repousse)
item_gem_lightning#Gemme de foudre
item_gem_lightning_desc#Attaque de foudre(rebondit)
item_fairy_ice#La fée Natacha
item_fairy_ice_desc#Vous aide dans le donjon
item_ring_ice#Anneau de saphir
item_ring_ice_desc#Augmente le pouvoir de glace
item_ring_stone#Anneau de quartz
item_ring_stone_desc#Augmente le pouvoir de pierre
item_ring_lightning#Anneau d'améthyste
item_ring_lightning_desc#Augmente le pouvoir de foudre
item_ring_illusion#Anneau d'obsidienne
item_ring_illusion_desc#Augmente le pouvoir d'illusion
item_spell_triple#Manuel de Triple-tir
item_spell_triple_desc#Tire trois projectiles
item_fairy_fire#La fée Alicia
item_fairy_fire_desc#Vous aide dans le donjon
item_fairy_target#La fée Scarlett
item_fairy_target_desc#Vous aide dans le donjon
item_robe_magician#Robe de magicien
item_robe_magician_desc#Augmente l'armure
item_gem_fire#Gemme de feu
item_gem_fire_desc#Attaque de feu (dégâts augmentés)
item_ring_fire#Anneau de rubis
item_ring_fire_desc#Augmente le pouvoir de feu
item_manual_staff#Manuel du bâton
item_manual_staff_desc#Augmente la portée
item_spell_teleport#Sort : Téléportation
item_spell_teleport_desc#Permet de se téléporter dans la pièce
item_spell_slime_explode#Sort: Invoquer Slime
item_spell_slime_explode_desc#Invoque un slime explosif
item_spell_fireball#Sort: Boule de feu
item_spell_fireball_desc#Envoie une boule de feu
item_spell_freeze#Sort: Geler monstres
item_spell_freeze_desc#Gèle les monstres
item_spell_earthquake#Sort: Séisme
item_spell_earthquake_desc#Fait trembler le donjon
item_spell_protection#Sort: Protection
item_spell_protection_desc#Invoque un bouclier magique
item_manual_slime#Manuel de maîtrise des slimes
item_manual_slime_desc#Les slimes ne vous visent plus
item_spell_dual_quick#Sort: Double-tir (rapide)
item_spell_dual_quick_desc#Tire deux projectiles (rapide)
item_spell_triple_quick#Sort: Triple-tir (rapide)
item_spell_triple_quick_desc#Tire trois projectiles (rapide)
item_manual_health#Manuel de santé
item_manual_health_desc#Gain de PV augmenté
item_rear_shot#Embout de bâton
item_rear_shot_desc#Tir arrière
item_book_random_shot#Sort: Tir aléatoire
item_book_random_shot_desc#Tire dans une direction aléatoire
item_spell_web#Sort: Toile d'araignée
item_spell_web_desc#Tire des toiles d'araignée
item_book_magic_i#Grimoire - Tome I
item_book_magic_i_desc#Réduit le délai entre deux sorts
item_book_magic_ii#Grimoire - Tome II
item_book_magic_ii_desc#Augmente la puissance du sort
item_sulfur#Soufre
item_sulfur_desc#Chance de faire exploser les cadavres
spell_teleport#téléportation
spell_slime_explode#invoquer slime
spell_fireball#boule de feu
spell_freeze#geler monstres
spell_earthquake#séisme
spell_protection#protection
spell_web#toile d'araignée
msg_level1_0#Votre quête commence !
msg_level1_1#"Les sorcières ont enlevé la fille de l'Archimage.
msg_level1_2#Je dois descendre dans leur antre et la secourir !"
msg_level2_0#La fille de l'Archimage
msg_level2_1#"J'arrive, je vais te sortir de là !
msg_level2_2#J'espère qu'elle est saine et sauve..."
msg_level3_0#La fille de l'Archimage
msg_level3_1#"Vous l'ai-je déjà dit ?
msg_level3_2#La fille de l'Archimage est la plus douce de l'Académie de Magie !"
msg_level4_0#L'Archimage
msg_level4_1#"L'Archimage est très sage, et est aussi mon mentor.
msg_level4_2#Il m'a appris que nous vivons dans le meilleur des monde."
msg_level5_0#La fille de l'Archimage
msg_level5_1#"Je me souviens d'elle et de moi, à l'Académie de Magie.
msg_level5_2#Enfants, nous jouions au mage guérisseur, hi hi !"
msg_level6_0#L'Archimage
msg_level6_1#"Mes parents sont décédés quand j'étais bébé.
msg_level6_2#L'Archimage m'a élevé comme si j'étais des siens."
msg_rats_bats_0#La vermine
msg_rats_bats_1#"Hey! Pourquoi faut-il toujours exterminer des rats et chauve-souris
msg_rats_bats_2#dans les Roguelikes ?"
msg_snakes_0#Les serpents
msg_snakes_1#"Je DETESTE les serpents!"
msg_witches_0#Les sorcières
msg_witches_1#"S.. Sorcières !!!"
msg_gold_0#Or
msg_gold_1#"♫ Money money money ♫
msg_gold_2#Je me demande si je peux acheter des trucs intéressants dans ce donjon..."
msg_familiar_0#Les fées
msg_familiar_1#"Oh ! De la compagnie !
msg_familiar_2#J'espère trouver d'autres alliés dans le donjon."
msg_butcher_0#Le Boucher porcin
msg_butcher_1#"Wow, ce gars est balèze !
msg_butcher_2#J'espère ne pas terminer en saucisses..."
msg_giant_slime_0#Le Slime géant
msg_giant_slime_1#"Je suis vert, énorme et je pue.
msg_giant_slime_2#Qui suis-je ?"
msg_cyclops_0#Le Cyclope de pierre
msg_cyclops_1#"Comment est-il entré dans cette pièce ?
msg_cyclops_2#Il est plus large que les portes !"
msg_wererat_0#Le Roi des Rats-garous
msg_wererat_1#"Il ressemble à ma vieille tante..."
msg_giant_spider_before_0#Une pièce vide
msg_giant_spider_before_1#"Y'a quelqu'un ? "
msg_giant_spider_after_0#L'Araignée géante
msg_giant_spider_after_1#"Je DETESTE les araignées"
msg_tuto_intro_0#Tutoriel: Bases
msg_tuto_intro_1#Utilisez ZQSD (standard) pour se déplacer dans le donjon.
msg_tuto_intro_2#Tirez avec les flèches (standard).
msg_tuto_items_0#Tutoriel: Objets
msg_tuto_items_1#Les objets améliorent le personnage ou lui octroient de nouveaux pouvoirs.
msg_tuto_items_2#trouvez ou achetez-en autant que possible.
msg_tuto_heal_0#Tutoriel: Santé
msg_tuto_heal_1#Vous avez perdu des points de vie.
msg_tuto_heal_2#Trouvez des moyens de guérir avant de mourir.
msg_tuto_shops_0#Tutoriel: Boutique
msg_tuto_shops_1#Vous pouvez trouver plusieurs boutiques dans le donjon.
msg_tuto_shops_2#Ici, vous dépenserez votre argent en objets et nourriture.
msg_tuto_boss_door_0#Tutoriel: Porte du Boss
msg_tuto_boss_door_1#D'ici, vous apercevez une porte dorée verrouillée.
msg_tuto_boss_door_2#Elle protège le Boss, trouvez la clef adéquate pour l'ouvrir.
msg_tuto_challenge_door_0#Tutoriel: Salle de Challenge
msg_tuto_challenge_door_1#Regardez la porte décorée ? C'est une salle de challenge.
msg_tuto_challenge_door_2#Vous pouvez y gagner des objets de valeur mais attention !
msg_tuto_shots_0#Tutoriel: Types de tir
msg_tuto_shots_1#Vous avez trouvé un nouveau type de tir.
msg_tuto_shots_2#Appuyez sur [sélection de tir] (standard: tab) pour changer de type de tir.
msg_tuto_spell_0#Tutoriel: Magie
msg_tuto_spell_1#Vous avez trouvé un nouveau sort.
msg_tuto_spell_2#Appuyez sur la touche de sort (standard: espace) pour le lancer.
msg_francky_0#La... Chose
msg_francky_1#Beurk... Il a l'air "fabriqué" à partir de morceaux de cadavres !
msg_tuto_tips_0#Tutoriel: Astuces
msg_tuto_tips_1#Le contrôle du temps (standard: Shift droit) permet de se déplacer plus vite.
msg_tuto_tips_2#Appuyez sur [Enter] pour fermer les boites de message.
enemy_type_bat#une chauve-souris
enemy_type_rat#un rat
enemy_type_rat_black#un rat noir
enemy_type_rat_helmet#un rat casqué
enemy_type_rat_black_helmet#un rat noir casqué
enemy_type_evil_flower#une fleur maléfique
enemy_type_evil_flower_ice#un edelweiss maléfique
enemy_type_snake#un serpent
enemy_type_snake_blood#un serpent de sang
enemy_type_slime#un slime
enemy_type_slime_red#un slime rouge
enemy_type_slime_blue#un slime bleu
enemy_type_slime_violet#un slime violet
enemy_type_imp_blue#un diablotin bleu
enemy_type_imp_red#un diablotin rouge
enemy_type_pumpkin#une citrouille démente
enemy_type_witch#une sorcière
enemy_type_witch_red#une sorcière rouge
enemy_type_cauldron#un chaudron
enemy_type_bubble#une bulle
enemy_type_boss_butcher#le Boucher porcin
enemy_type_boss_slime_giant#le Slime géant
enemy_type_boss_cyclops#le Cyclope de pierre
enemy_type_boss_rat_king#le Roi des Rats-garous
enemy_type_boss_spider_giant#l'Araignée géante
enemy_type_green_rat#un rat vert
enemy_type_rock_falling#un rocher tombant
enemy_type_rock_missile#un rocher lancé
enemy_type_spider_egg#un oeuf d'araignée tombant
enemy_type_spider_little#une araignée
enemy_type_spider_web#une toile d'araignée
enemy_type_ghost#un fantôme
enemy_type_zombie#un zombie
enemy_type_zombie_dark#un zombie sombre
enemy_type_francky#la Chose
enemy_type_himself#lui-même
source_explosion#une explosion
source_poison#du poison
dc_certificate#Certificat de décès
dc_killed_by#Tué par
dc_died_level#Mort au niveau
dc_after#après
dc_minutes#min. de jeu
dc_killed_monsters#Monstres vaincus
dc_gold#Or
dc_challenges#Défis relevés
player_name#Nom du joueur
player_name_desc#Modifier le nom du joueur
hi_scores#Hall of fame
hi_scores_desc#Meilleurs scores
credits#Crédits
credits_desc#Ecran des crédits
volume_sound#Volume du son
volume_sound_desc#Utiliser les flèches gauche/droite pour modifier le volume
volume_music#Volume de la musique
certificate_capture#Appuyez sur [F1] pour faire une copie du certificat dans un PNG
certificate_saved#Enregistré sous
msg_tuto_temple_0#Tutoriel: Temple
msg_tuto_temple_1#Au temple, vous pouvez vénérer des divinités et faire des donations.
msg_tuto_temple_2#Les divinités vous apporte leur aide et des bonus.
key_interact#interaction
interact_worship#Vénérer
interact_shop#Acheter
interact_donate#Donner 10 pièces
interact_donate_fail#(pas assez d'argent)
worshipping#Vous êtes maintenant un disciple de
divinity_healer_0#Pomonos le Juste
divinity_healer_1#Gardien de la vertu et protecteur des faibles.
divinity_healer_2#Il apprécie les dons, la souffrance, et le combat contre le mal.
divinity_fighter_0#Tello le Puissant
divinity_fighter_1#Seigneur des guerriers et de la force.
divinity_fighter_2#Il aime voir tomber les monstres et les titans.
divinity_ice_0#Mister Snow
divinity_ice_1#Divinité du Froid.
divinity_ice_2#Il apprécie l'usage de la glace.
divinity_stone_0#Gaya
divinity_stone_1#Divinité de la Terre et de la Pierre.
divinity_stone_2#Il apprécie l'usage de la pierre.
divinity_intervention_0#Intervention divine
divinity_intervention_1#intervient en votre faveur.
divinity_gift_0#Présent divin
divinity_gift_1#vous offre un présent.
item_floor_map#Carte du donjon
item_floor_map_desc#Révèle la carte de cet étage
item_alcohol#Absinthe
item_alcohol_desc#Vous rend temporairement plus fort
achiev_rats#Rodenticide
achiev_rats_desc#Tuer 250 rats
achiev_100#Radin
achiev_100_desc#Posséder 100 pièces
achiev_piety_max#Fidèle
achiev_piety_max_desc#Atteindre le niveau max de piété
achiev_4_hits#Brochette
achiev_4_hits_desc#Toucher 4 monstres avec le même tir
achiev_complete_set#Habillé
achiev_complete_set_desc#Posséder robe + chapeau + bottes + gants
achiev_giant_slime#Pourfendeur de Slime Géant
achiev_giant_slime_desc#Tuer le Slime Géant
achiev_cyclops#Pourfendeur de Cyclope
achiev_cyclops_desc#Tuer le Cyclope de pierre
achiev_rat_king#Pourfendeur de Rat-Garou
achiev_rat_king_desc#Tuer le Rat-Garou
achiev_giant_spider#Pourfendeur d'Araignée Géante
achiev_giant_spider_desc#Tuer l'Araignée Géante
achiev_francky#Pourfendeur de Chose
achiev_francky_desc#Tuer la Chose
achiev_butcher#Pourfendeur de Boucher
achiev_butcher_desc#Tuer le Boucher porcin
achiev_witches#Inquisiteur
achiev_witches_desc#Tuer 50 sorcières
achiev_eggs#Mauvaise omelette
achiev_eggs_desc#Détruire 100 oeufs
achiev_vampire#Chasseur de vampires
achiev_vampire_desc#Tuer le Vampire
achiev_challenges#Gladiateur
achiev_challenges_desc#Relever 4 défis
achiev_win#Vivre son rêve
achiev_win_desc#Terminer la démo
func_achiev_screen#Ecran des succès
func_achiev_all#Description des succès
func_double_spell#Drop de deux sorts
enemy_type_evil_flower_fire#une fleur de feu maléfique
enemy_type_bat_skeleton#une chauve-souris squelette
enemy_type_slime_large#un gros slime
enemy_type_slime_blue_large#un gros slime bleu
enemy_type_slime_red_large#un gros slime rouge
enemy_type_slime_violet_large#un gros slime violet
enemy_type_bogeyman#un croque-mitaine
enemy_type_cauldron_elemental#un chaudron élémentaire
menu_achievements#Succès
menu_achievements_desc#Affiche les succès
achievement_complete#Succès débloqué !
item_spell_flower#Sort: Invoquer fleur magique
item_spell_flower_desc#Invoque une fleur magique
item_rapid_shots#Manuel de tir rapide
item_rapid_shots_desc#La mitraillette du magicien
item_fairy_poison#La fée Vicky
item_fairy_poison_desc#Vous aide dans le donjon
item_luck#Trèfle à 4 feuilles
item_luck_desc#Augmente temporairement l'or trouvé
item_fairy_powder#Poussière de fée
item_fairy_powder_desc#Excite temporairement les fées
item_ring_poison#Anneau d'émeraude
item_ring_poison_desc#Augmente le pouvoir de poison
item_gem_poison#Gemme de poison
item_gem_poison_desc#Attaque de poison
item_book_prayer_i#Livre de prières - Tome I
item_book_prayer_i_desc#Augmente la piété
item_book_prayer_ii#Livre de prières - Tome II
item_book_prayer_ii_desc#Reduit les coûts en piété
item_pet_slime#Slime de compagnie
item_pet_slime_desc#Il vous suit et se bat à vos côtés
item_hat_advanced#Chapeau de Warlock
item_hat_advanced_desc#Résistance élémentaire + cadence de tir+
item_robe_advanced#Robe magique de choc
item_robe_advanced_desc#Repousse + armure augmentée
item_critical#Barbe factice de mage
item_critical_desc#Chance de dégâts critiques (x2)
item_critical_advanced#Barbe factice d'Archimage
item_critical_advanced_desc#Chance de dégâts critiques (x3)
item_spell_fairy#Sort: Transformation en fée
item_spell_fairy_desc#Transforme le magicien en fée
item_gloves_advanced#Gants de ranger
item_gloves_advanced_desc#Pièges désactivés + cadence de tir augmentée
item_boots_advanced#Bottes de ranger
item_boots_advanced_desc#Augmente la vitesse
divinity_leveling_0#Niveau supérieur de dévotion !
div_fighter_lvl#augmente les dégâts
div_healer_lvl_2#régénère
div_healer_lvl_3#augmente la régénération
div_ice_lvl_3#augmente la résistance au froid
div_ice_lvl_4#augmente le niveau d'attaque de glace
div_ice_lvl_5#augmente l'immunité de gel
div_stone_lvl_3#augmente la résistance à la pierre
div_stone_lvl_4#augmente le niveau d'attaque de pierre
div_stone_lvl_5#augmente l'immunité de repoussement
enemy_vampire#Vampire
enemy_type_vampire#le Vampire
msg_tuto_achiev_0#Tutorial: Succès
msg_tuto_achiev_1#Vous avez obtenu un succès.
msg_tuto_achiev_2#Ils débloquent des objets ou fonctionnalités.
msg_vampire_0#Le Vampire
msg_vampire_1#J'ai oublié d'emporter de l'ail !
msg_level7_0#La fille de l'Archimage
msg_level7_1#"Je me rapproche encore.
msg_level7_2#Elle ne peut plus être trop loin d'ici."
divinity_air_0#Ostram
divinity_air_1#Divinité de l'air et du tonnerre.
divinity_air_2#Il apprécie l'usage de la foudre.
div_air_lvl#augmente votre vitesse
div_air_lvl_4#augmente le niveau d'attaque de foudre
config_joystick#Configurer le joystick
redef_joystick#Redéfinir les contrôles joystick
joystick_configuration#Configuration du joystick
joystick_configuration_desc#Configurez les actions du joystick
joystick_configuration_insert#[appui bouton]
joystick_not_found#Aucun joystick connecté !
item_scroll_revelation#Parchemin de révélation
item_scroll_revelation_desc#Révéler secrets et effets des potions
item_potion_01#Potion rouge
item_potion_02#Potion verte
item_potion_03#Potion dorée
item_potion_04#Potion bleue
item_potion_05#Potion violette
item_potion_06#Potion rose
item_potion_07#Potion orange
item_potion_08#Potion turquoise
item_potion_09#Potion marron
item_potion_10#Potion sombre
item_potion_desc#Effet inconnu
item_potion_health#Potion de guérison
item_potion_health_desc#Guérit 18 PV
item_potion_poison#Potion de poison
item_potion_poison_desc#Empoisonne le buveur
item_potion_speed#Potion de vitesse
item_potion_speed_desc#Augmente temporairement la vitesse
item_potion_slow#Potion de lenteur
item_potion_slow_desc#Réduit temporairement la vitesse
item_potion_weakness#Potion de faiblesse
item_potion_weakness_desc#Réduit temporairement les dégâts
item_potion_strength#Potion de force
item_potion_strength_desc#Augmente temporairement les dégâts
item_potion_oblivion#Potion d'oubli
item_potion_oblivion_desc#Oublie la carte et les potions
item_potion_confusion#Potion de confusion
item_potion_confusion_desc#Rend confus
item_potion_cure#Potion Tabula Rasa
item_potion_cure_desc#Annule les effets du buveur
item_potion_rage#Potion de rage
item_potion_rage_desc#Tire des boules de feu
item_bag#Sacoche
item_bag_desc#Plus de place pour les consommables
msg_potion_id_0#Identification de potion
msg_potion_id_1#Vous avez identifié:
msg_tuto_consumables_0#Tutoriel: Consommables
msg_tuto_consumables_1#[num1-num4] pour utiliser un objet consommable.
msg_tuto_consumables_2#[left shift][num1-num4] pour le jeter.
msg_tuto_potions_0#Tutoriel: Potions
msg_tuto_potions_1#Vous avez trouvé une potion.
msg_tuto_potions_2#Vous devez la boire ou l'identifier pour connaitre son effet.
secret_found#Salle secrète découverte!
enemy_type_sausage#une saucisse
enemy_type_spider_tarantula#une tarentule
effect_speed#Vitesse augmentée !
effect_slow#Vitesse réduite !
effect_strength#Dégâts augmentés !
effect_weakness#Dégâts réduits !
effect_confusion#Confus !
effect_forget#Vous oubliez !
effect_cure#Tabula rasa !
effect_rage#Raaaage !
item_book_alchemy#Manuel d'alchimie
item_book_alchemy_desc#Révèle les effets de quelques potions
item_fairy_stone#Fairy Cassandre
item_fairy_stone_desc#Vous aide dans le donjon
item_merchant#Sceau des marchand
item_merchant_desc#Meilleurs prix en magasin
item_spell_time#Sort : Arrêter le temps
item_spell_time_desc#Réduit le temps
item_side_shots#Manuel de tirs latéraux
item_side_shots_desc#Tire sur les côtés
item_spell_lightning#Sort: Tonnerre
item_spell_lightning_desc#Invoque un éclair
item_belt_advanced#Ceinture noire
item_belt_advanced_desc#Cadence de tir des fées +
item_rear_shot_advanced#Embout de bâton en argent
item_rear_shot_advanced_desc#Double tir arrière
item_amulet_retaliation#Amulette de vengeance
item_amulet_retaliation_desc#Contre-attaque
item_rage_amulet#Amulette de rage
item_rage_amulet_desc#Tire des boules de feu
achiev_fairies#Charlie's Angels
achiev_fairies_desc#Trouver 3 fées
achiev_ghost#Ghostbuster
achiev_ghost_desc#Tuer 50 fantômes
achiev_noob#Vrai noob
achiev_noob_desc#Mourir au premier niveau
achiev_no_damage#Balèze
achiev_no_damage_desc#Traverser un niveau sans perdre de PV
achiev_no_damage2#Trop balèze
achiev_no_damage2_desc#Traverser 2 niveaux sans perdre de PV
achiev_no_damage3#Trop trop balèze
achiev_no_damage3_desc#Traverser 3 niveaux sans perdre de PV
achiev_apostate#Apostat
achiev_apostate_desc#Rejeter une divinité niveau 4
achiev_fanatic#Fanatique
achiev_fanatic_desc#Donner 100 pièces d'or au temple
func_nothing#Que dalle !
state_frozen#Froid
state_slow#Lenteur
state_speed#Vitesse
state_poison#Poison
state_weakness#Faiblesse
state_strength#Force
state_rage#Rage
state_confusion#Confusion
state_time#Temps figé
state_div_protection#Protection (div)
state_div_speed#Vitesse (div)
state_div_firerate#Cadence de tir+ (div)
state_div_damage#Dégâts+ (div)
dc_victory#Attestation de réussite
dc_after_victory#Victoire après
player#joueur
players#joueurs
ui_base_damage#Dégâts de base
ui_fire_rate#Cadence de tir
ui_second#seconde
ui_temple_donation#Donation au temple
[german]
start_new_game#Neues Spiel starten
start_desc#Alter Speicherstand wird überschrieben !
restore#Speicherstand wiederherstellen
at#um
level#Level
begin_journey#Beginne deine Reise in einem neuen Verlies
config_keys#Steuerung
redef_input#Ändere die Tastenbelegung
exit_game#Spiel verlassen
return_to_desktop#Kehre zum Desktop zurück
key_configuration#Tastenbelegung
key_configuration_desc#Bitte belege die Tasten
key_configuration_insert#[Taste einsetzen]
key_configuration_done#FERTIG
key_move_up#Nach oben
key_move_down#Nach unten
key_move_left#Nach links
key_move_right#Nach rechts
key_fire_up#Schieße oben
key_fire_down#Schieße unten
key_fire_left#Schieße links
key_fire_right#Schieße rechts
key_fire_select#Schieße Auswahl
key_time#Zeitsteuerung
key_fire#Schießen (ein Knopf)
key_spell#Verzaubern
configure_game#Optionen
configure_game_desc#Ändere die Spieloptionen
config_back#Zurück
config_back_desc#Zurück zum Hauptmenü
config_lang#Sprache
config_lang_desc#Wähle mit linker und rechter Pfeiltaste aus
english#Englisch
french#Französisch
german#Deutsch
keys_move#Bewegen
keys_fire#Schießen
keys_select_1#Waffe
keys_select_2#wechseln
keys_time#Zeitsteuerung
play_again#Drücke [ENTER], um nochmal zu spielen / [ESC], um ins Hauptmenü zurückzukehren
items_found#Items
monsters_killed#Monster
congratulations_1#HERZLICHEN GLÜCKWUNSCH !
congratulations_2#Du hast dich der Demo Version gestellt und die Bosse getötet !
congratulations_3#Bis bald für neue Abenteuer !
by#von
critical#KRITISCH
poison#GIFT
merchant_speech_0#Bester Preis im gesamten Verlies!
merchant_speech_1#Willkommen im Pawn'n'Witches Shop!
merchant_speech_2#Schau dir unsere Waren an!
enemy_butcher#Schweinemetzger
enemy_giant_slime#Gigantischer Schleim
enemy_cyclops#Steinzyklop
enemy_rat_king#Werrattenkönig
enemy_giant_spider#Gigantische Spinne
item_copper#Kupfermünze
item_copper_desc#Eine Kupfermünze (Wert 1)
item_silver#Silbermünze
item_silver_desc#Eine Silbermünze (Wert 5)
item_gold#Goldmünze
item_gold_desc#Eine Goldmünze (Wert 10)
item_titan_heart#Herz des Titanen
item_titan_heart_desc#Erhöht die maximalen TP
item_elven_cookie#Elfenkeks
item_elven_cookie_desc#Regeneriert und erhöht die max. TP
item_apple#Apfel
item_apple_desc#Stellt 3 TP wieder her
item_bread#Brot
item_bread_desc#Stellt 7 TP wieder her
item_cheese#Käse
item_cheese_desc#Stellt 15 TP wieder her
item_boss_key#Boss-Schlüssel
item_boss_key_desc#Öffnet die Boss-Tür
item_sorcerer_hat#Hut des Zauberers
item_sorcerer_hat_desc#Erhöht die Feuerrate
item_velvet_boots#Samtschuhe
item_velvet_boots_desc#Erhöht die Geschwindigkeit
item_spell_dual#Zauber : Doppelter Schuss
item_spell_dual_desc#Schießt zwei Schüsse
item_displacement_gloves#Verschiebungs-Handschuhe
item_displacement_gloves_desc#Die Geschwindigkeit des Spielers beeinflusst die der Schüsse
item_staff#Elfenbeinstab
item_staff_desc#Erhöht Schussgeschwindigkeit und Schaden
item_fairy#Lilly-Fee
item_fairy_desc#Hilft dir im Verlies
item_leather_belt#Ledergürtel
item_leather_belt_desc#Erhöht die Feuerrate
item_blood_snake#Blutschlange
item_blood_snake_desc#Erhöht den Schaden
item_gem_ice#Eisjuwel
item_gem_ice_desc#Eisangriff (einfrieren)
item_gem_illusion#Illusionsjuwel
item_gem_illusion_desc#Illusionsangriff (ignoriert Wände)
item_gem_stone#Steinjuwel
item_gem_stone_desc#Steinangriff (rückstoßen)
item_gem_lightning#Blitzjuwel
item_gem_lightning_desc#Blitzangriff (abprallen)
item_fairy_ice#Natasha-Fee
item_fairy_ice_desc#Hilft dir im Verlies
item_ring_ice#Saphir Ring
item_ring_ice_desc#Erhöht die Eiskraft
item_ring_stone#Quarzring
item_ring_stone_desc#Erhöht die Steinkraft
item_ring_lightning#Amethystring
item_ring_lightning_desc#Erhöht die Blitzkraft
item_ring_illusion#Obsidianring
item_ring_illusion_desc#Erhöht die Illusionskraft
item_spell_triple#Zauber : Dreifacher Schuss
item_spell_triple_desc#Schießt drei Schüsse
item_brooch_finesse#Falscher Erzmagierbart
item_brooch_finesse_desc#Kritische Trefferchance (x2)
item_fairy_fire#Alicia-Fee
item_fairy_fire_desc#Hilft dir im Verlies
item_fairy_target#Scarlett-Fee
item_fairy_target_desc#Hilft dir im Verlies
item_robe_magician#Robe des Zauberers
item_robe_magician_desc#Erhöht die Rüstung
item_gem_fire#Feuerjuwel
item_gem_fire_desc#Feuerangriff (mehr Schaden)
item_ring_fire#Rubin Ring
item_ring_fire_desc#Erhöht die Feuerkraft
item_manual_staff#Handbuch des Zauberstabes
item_manual_staff_desc#Erhöht die Reichweite
item_spell_teleport#Spell: Teleport
item_spell_teleport_desc#Cast to teleport within the room
item_spell_slime_explode#Spell: Invoke Slime
item_spell_slime_explode_desc#Cast to invoke an explosive slime
spell_teleport#Teleportation
spell_slime_explode#invoke slime
item_spell_fireball#Zauber: Feuerball
item_spell_fireball_desc#Zauber, um einen Feuerball zu beschwören
item_spell_freeze#Zauber: Monster einfrieren
item_spell_freeze_desc#Zauber, um Monster einzufrieren
item_spell_earthquake#Zauber: Erdbeben
item_spell_earthquake_desc#Zauber, um das Verlies durchzuschütteln
item_spell_protection#Zauber: Magisches Schild
item_spell_protection_desc#Zauber, um ein magisches Schild zu beschwören
spell_fireball#Feuerball
spell_freeze#Monster einfrieren
spell_earthquake#Erdbeben
spell_protection#Magisches Schild
msg_level1_0#Deine Aufgabe beginnt!
msg_level1_1#"Hexen haben die Tochter des Erzmagiers entführt und sie in ihr Lager gebracht.
msg_level1_2#Ich muss sie retten!"
msg_level2_0#Die Tochter des Erzmagiers
msg_level2_1#"Ich komme sofort, um dich zu befreien !
msg_level2_2#Hoffentlich ist sie wohlbehalten..."
msg_level3_0#Die Tochter des Erzmagiers
msg_level3_1#"Hab ich es dir je erzählt?
msg_level3_2#Die Tochter des Erzmagiers ist das süßeste Mädchen der magischen Akademie!"
msg_level4_0#Der Erzmagier
msg_level4_1#"Der Erzmagier ist ein sehr weiser Mann, und zugleich mein Mentor.
msg_level4_2#Er hat mich gelehrt, dass wir auf der besten Welt leben."
msg_level5_0#Die Tochter des Erzmagiers
msg_level5_1#"Ich erinnere mich an die Tochter des Erzmagiers und mich, damals auf der magischen Akademie.
msg_level5_2#Wir haben beim Heilmagier gespielt, hi hi !"
msg_level6_0#Der Erzmagier
msg_level6_1#"Meine Eltern starben, als ich noch ein Baby war.
msg_level6_2#Der Erzmagier hat mich wie seinen eigenen Sohn aufgezogen."
msg_rats_bats_0#Monster
msg_rats_bats_1#"Hey! Warum müssen wir immer Ratten und Fledermäuse ausrotten
msg_rats_bats_2#in diesen Schurken Spielen ?"
msg_snakes_0#Schlangen
msg_snakes_1#"Ich HASSE Schlangen!"
msg_witches_0#Hexen
msg_witches_1#"S... Hexen !!!"
msg_gold_0#Gold
msg_gold_1#"♫ Money money money ♫
msg_gold_2#Ich frage mich, ob ich nützliche Gegenstände in diesem Verlies kaufen kann..."
msg_familiar_0#Feen
msg_familiar_1#"Oh! Ich habe Gesellschaft!
msg_familiar_2#Ich hoffe ich finde noch andere Verbündete im Verlies."
msg_butcher_0#Der Schweinemetzger
msg_butcher_1#"Wow, das ist ein großer Typ!
msg_butcher_2#Ich hoffe ich werde kein Hackfleisch..."
msg_giant_slime_0#Der gigantische Schleim
msg_giant_slime_1#"Ich bin grün, riesig und stinke.
msg_giant_slime_2#Was ich bin ?"
msg_cyclops_0#Der Steinzyklop
msg_cyclops_1#"Wie ist es in dem Raum gelaufen?
msg_cyclops_2#Es ist größer und länger als das Tor!"
msg_wererat_0#Die Werratte
msg_wererat_1#"Sieht aus wie meine alte Tante..."
msg_giant_spider_before_0#Der leere Raum
msg_giant_spider_before_1#"Ist hier jemand? "
msg_giant_spider_after_0#Die riesige Spinne
msg_giant_spider_after_1#"Ich HASSE Spinnen"
msg_tuto_intro_0#Tutorial: Wesentliches
msg_tuto_intro_1#Benutze WASD (Standard), um dich im Verlies zu bewegen.
msg_tuto_intro_2#Feuere mit den Pfeiltasten (Standard).
msg_tuto_items_0#Tutorial: Gegenstände
msg_tuto_items_1#Gegenstände verbessern deinen Charakter oder geben dir neue Kräfte.
msg_tuto_items_2#Finde oder kaufe so viele du kannst.
msg_tuto_heal_0#Tutorial: Leben
msg_tuto_heal_1#Du hast einige Trefferpunkte verloren.
msg_tuto_heal_2#Finde etwas um dich zu heilen, bevor du stirbst.
msg_tuto_shops_0#Tutorial: Geschäfte
msg_tuto_shops_1#Du kannst viele Geschäfte im Verlies finden.
msg_tuto_shops_2#Hier kannst du Geld ausgeben, um Gegenstände oder Essen zu kaufen.
msg_tuto_boss_door_0#Tutorial: Boss-Tür
msg_tuto_boss_door_1#Von hier aus kannst du eine geschlossene, goldene Tür sehen.
msg_tuto_boss_door_2#Hier ist der Boss von dieser Etage, aber du brauchst den goldenen Schlüssel, um die Tür zu öffnen.
msg_tuto_challenge_door_0#Tutorial: Raum der Herausforderung
msg_tuto_challenge_door_1#Schau auf die dekorative Tür. Es ist ein Raum der Herausforderung.
msg_tuto_challenge_door_2#Du kannst hier wertvolle Belohnungen erhalten, aber sei vorsichtig!
msg_tuto_shots_0#Tutorial: Schussarten
msg_tuto_shots_1#Du hast eine neue Schussart gefunden.
msg_tuto_shots_2#Drücke [Waffe wechseln] (Standard: Tab), um zwischen den Schussarten zu wechseln.
msg_tuto_spell_0#Tutorial: Zauber
msg_tuto_spell_1#Du hast einen magischen Zauber gefunden.
msg_tuto_spell_2#Drücke die Zaubertaste (Standard: Leertaste), um einen Zauber zu beschwören.
enemy_type_bat#eine Fledermaus
enemy_type_rat#eine Ratte
enemy_type_rat_black#eine schwarze Ratte
enemy_type_rat_helmet#eine Ratte mit Helm
enemy_type_rat_black_helmet#eine schwarze Ratte mit Helm
enemy_type_evil_flower#eine böse Blume
enemy_type_evil_flower_ice#eine böse Edelweiß
enemy_type_snake#eine Schlange
enemy_type_snake_blood#eine Blutschlange
enemy_type_slime#ein Schleim
enemy_type_slime_red#ein roter Schleim
enemy_type_slime_blue#ein blauer Schleim
enemy_type_slime_violet#ein violetter Schleim
enemy_type_imp_blue#ein blauer Imp
enemy_type_imp_red#ein roter Imp
enemy_type_pumpkin#ein verrückter Kürbis
enemy_type_witch#eine Hexe
enemy_type_witch_red#eine rote Hexe
enemy_type_cauldron#ein großer Kessel
enemy_type_bubble#eine Blase
enemy_type_boss_butcher#der Schweinemetzger
enemy_type_boss_slime_giant#der Gigantische Schleim
enemy_type_boss_cyclops#der Steinzyklop
enemy_type_boss_rat_king#der Werrattenkönig
enemy_type_boss_spider_giant#die Gigantische Spinne
enemy_type_green_rat#eine grüne Ratte
enemy_type_rock_falling#ein fallender Fels
enemy_type_rock_missile#ein geworfener Fels
enemy_type_spider_egg#ein fallendes Spinnenei
enemy_type_spider_little#eine Spinne
enemy_type_spider_web#ein Spinnennetz
enemy_type_ghost#ein Geist
enemy_type_zombie#ein Zombie
enemy_type_zombie_dark#ein dunkler Zombie
enemy_type_francky#das Ding
enemy_type_himself#selbst
source_explosion#Explosion
source_poison#Gift
enemy_francky#Das Ding
enemy_francky_head#Ding's Kopf
item_apple_green#Grüner Apfel
item_apple_green_desc#Regeneriert 3 TP und heilt Vergiftungen
item_manual_slime#Handbuch der Schleimbeherrschung
item_manual_slime_desc#Schleime zielen nicht mehr auf dich
item_spell_dual_quick#Zauberbuch der schnellen Doppelschüsse
item_spell_dual_quick_desc#Schießt zwei Schüsse (schnell)
item_spell_triple_quick#Zauberbuch der schnellen Dreifachschüsse
item_spell_triple_quick_desc#Schießt drei Schüsse (schnell)
item_manual_health#Gesundheitshandbuch
item_manual_health_desc#Gesunde Gegenstände bringen mehr TP
item_rear_shot#Mitarbeiter Kappe
item_rear_shot_desc#Schießt einen Rückwärtsschuss
item_book_random_shot#Zauberbuch der Zufallschüsse
item_book_random_shot_desc#Schießt einen Schuss in eine zufällige Richtung
item_spell_web#Zauber: Spinnennetz
item_spell_web_desc#Zaubere um ein Spinnennetz zu werfen
item_book_magic_i#Zauberbuch - Wälzer I
item_book_magic_i_desc#Verringert die Abklingzeit der Zauber
item_book_magic_ii#Zauberbuch - Wälzer II
item_book_magic_ii_desc#Erhöht die Kraft der Zauber
item_sulfur#Schwefel
item_sulfur_desc#Kann Leichen zum Explodieren bringen
spell_web#Spinnennetz
tuto_reset#Tutorial zurücksetzen
tuto_reset_desc#Setzt das Tutorial zurück
msg_francky_0#Das... Ding
msg_francky_1#Ihh... Es sieht aus als wäre es aus verschiedenen Körperteilen gemacht !
msg_tuto_tips_0#Tutorial: Tipps
msg_tuto_tips_1#Benutze die Zeitkontrolle (Standard: RShift) um schneller zu reisen.
msg_tuto_tips_2#Drücke [Enter] um Nachrichtenboxen sofort zu schließen.
menu_continue#Fortfahren
menu_continue_desc#Mit dem Spiel fortfahren
menu_save_quit#Speichern und beenden
menu_save_quit_desc#Speichern und zum Hauptbildschirm zurückkehren
menu_quit#Beenden ohne speichern
menu_quit_desc#Zum Hauptbildschirm zurückkehren ohne speichern
inventory#Inventar
dc_certificate#Todeszertifikat
dc_killed_by#Getötet hat dich
dc_died_level#Gestorben auf Level
dc_after#nach
dc_minutes#Min. vom Spiel
dc_killed_monsters#Besiegte Monster
dc_gold#Gold
dc_challenges#Abgeschlossene Herausforderungen
player_name#Spielername
player_name_desc#Ändere den Spielernamen
hi_scores#Ruhmeshalle
hi_scores_desc#Highscores
credits#Credits
credits_desc#Credits Bildschirm
volume_sound#Sound Lautstärke
volume_sound_desc#Benutze die linke und rechte Pfeiltaste um die Lautstärke zu regeln
volume_music#Musik Lautstärke
certificate_capture#Drücke [F1] um das Zertifikat als PNG Bild zu speichern
certificate_saved#Speicher auf
msg_tuto_temple_0#Tutorial: Tempel
msg_tuto_temple_1#Der Tempel ist der Ort, wo man zu einem Gott beten oder spenden kann.
msg_tuto_temple_2#Götter können dir helfen und Boni vergeben.
key_interact#interagiere
interact_worship#Anbetung
interact_shop#Kaufe
interact_donate#Spende 10 Gold
interact_donate_fail#(nicht genug Gold)
worshipping#Du bist nun ein Anbeter von
divinity_healer_0#Pomonos der Rechte
divinity_healer_1#Wächter des Rechts und Schutzpatron der Armen.
divinity_healer_2#Er würdigt Spenden, Leid und den Kampf gegen das Böse.
divinity_fighter_0#Tello der Starke
divinity_fighter_1#Gebieter der Kämpfer und der Starken.
divinity_fighter_2#Er würdigt im Kampf gefallene Monster und Titanen.
divinity_ice_0#Mister Schnee
divinity_ice_1#Gott der Kälte.
divinity_ice_2#Er würdigt den Einsatz von Eis.
divinity_stone_0#Gaya
divinity_stone_1#Gott des Gesteines und der Erde.
divinity_stone_2#Er würdigt den Einsatz von Gestein.
divinity_intervention_0#Göttliche Intervention
divinity_intervention_1#schreitet zu deinem Gunsten ein.
divinity_gift_0#Göttliche Gabe
divinity_gift_1#gewährt dir ein Geschenk.
item_floor_map#Verlieskarte
item_floor_map_desc#Offenbart die Karte der derzeitigen Etage
item_alcohol#Absinth
item_alcohol_desc#erhöht vorübergehend den Schaden
achiev_rats#Rattengift
achiev_rats_desc#Töte 250 Ratten
achiev_100#Geizig
achiev_100_desc#Sammle 100 goldene Münzen
achiev_piety_max#Treu
achiev_piety_max_desc#Erreiche die höchste Pietätsstufe
achiev_4_hits#Brochette
achiev_4_hits_desc#Verletze 4 Monster mit demselben Schuss
achiev_complete_set#Gekleidet
achiev_complete_set_desc#Rüste aus: Mantel + Hut + Schuhe + Handschuhe
achiev_giant_slime#Gigantischer Schleim Mörder
achiev_giant_slime_desc#Töte den gigantischen Schleim
achiev_cyclops#Zyklopen Mörder
achiev_cyclops_desc#Töte den Steinzyklopen
achiev_rat_king#Werratten Mörder
achiev_rat_king_desc#Töte den Werrattenkönig
achiev_giant_spider#Gigantischer Spinnen Mörder
achiev_giant_spider_desc#Töte die gigantische Spinne
achiev_francky#Ding Mörder
achiev_francky_desc#Töte das Ding
achiev_butcher#Metzger Mörder
achiev_butcher_desc#Töte den Metzger
achiev_witches#Inquisitor
achiev_witches_desc#Töte 50 Hexen
achiev_eggs#Schlechtes Omelett
achiev_eggs_desc#Zerstöre 100 Eier
achiev_vampire#Vampirjäger
achiev_vampire_desc#Töte den Vampier
achiev_challenges#Gladiator
achiev_challenges_desc#Schließe 4 Herrausforderungen ab
achiev_win#Lebe einen Traum
achiev_win_desc#Beende die Demo
func_achiev_screen#Erfolge Bildschirm
func_achiev_all#Alle Beschreibungen der Erfolge
func_double_spell#Zwei Zauber fallen
enemy_type_evil_flower_fire#eine böse Feuerplanze
enemy_type_bat_skeleton#eine Skelettfledermaus
enemy_type_slime_large#ein riesiger Schleim
enemy_type_slime_blue_large#ein riesiger blauer Schleim
enemy_type_slime_red_large#ein riesiger roter Schleim
enemy_type_slime_violet_large#ein riesiger violetter Schleim
enemy_type_bogeyman#ein Schreckgespenst
enemy_type_cauldron_elemental#ein elementarer Kessel
menu_achievements#Erfolge
menu_achievements_desc#Zeige die Erfolge
achievement_complete#Erfolg freigeschaltet!
item_spell_flower#Zauber: Rufe magische Blume
item_spell_flower_desc#Benutze, um eine magische Blume zu rufen
item_rapid_shots#Zauberbuch des schnellen Schusses
item_rapid_shots_desc#Zauberer Maschinengewehr
item_fairy_poison#Fee Vicky
item_fairy_poison_desc#Hilft dir im Verlies
item_luck#Vierblättriges Kleeblatt
item_luck_desc#Erhöht kurzfristig die Chance auf Gold
item_fairy_powder#Feenstaub
item_fairy_powder_desc#Begeistert die Feen kurzfristig
item_ring_poison#Smaragdring
item_ring_poison_desc#Erhöht die Kraft des Giftes
item_gem_poison#Giftkristall
item_gem_poison_desc#Giftangriff
item_book_prayer_i#Buch der Beter - Wälzer I
item_book_prayer_i_desc#Erhöht die Pietät
item_book_prayer_ii#Buch der Beter - Wälzer II
item_book_prayer_ii_desc#Reduziert Kosten für die Pietät
item_pet_slime#Schleimhaustier
item_pet_slime_desc#Es wird dir folgen und helfen
item_hat_advanced#Hut des Hexenmeisters
item_hat_advanced_desc#Elementare Resistenz + Feuerate erhöht
item_robe_advanced#Magischer Mantel des Schocks
item_robe_advanced_desc#Reflecktiert und erhöht die Rüstung
item_critical#Falscher magischer Bart
item_critical_desc#Kritische Trefferchance (x2)
item_critical_advanced#Falscher Erzmagier Bart
item_critical_advanced_desc#Kritische Trefferchance (x3)
item_spell_fairy#Spell: Feentransmutation
item_spell_fairy_desc#Benutze, um dich in eine Fee zu verwandeln
item_gloves_advanced#Ranger Handschuhe
item_gloves_advanced_desc#Entschärft Fallen und erhöhte Schussrate
item_boots_advanced#Ranger Schuhe
item_boots_advanced_desc#Erhöhte Geschwindigkeit
divinity_leveling_0#Hingabe Aufstieg!
div_fighter_lvl#erhöht deinen Schaden
div_healer_lvl_2#gibt dir Regeneration
div_healer_lvl_3#erhöht deine Regeneration
div_ice_lvl_3#erhöht deine Eisresistenz
div_ice_lvl_4#erhöht deinen Eisangriff
div_ice_lvl_5#erhöht deine Frostimmunität
div_stone_lvl_3#erhöht deine Steinresistenz
div_stone_lvl_4#erhöht deinen Steinangriff
div_stone_lvl_5#erhöht deine Rückstoßimmunität
enemy_vampire#Vampir
enemy_type_vampire#der Vampir
msg_tuto_achiev_0#Tutorial: Erfolge
msg_tuto_achiev_1#Du hast einen Erfolg abgeschlossen.
msg_tuto_achiev_2#Sie offenbaren neue Gegenstände oder Boni.
msg_vampire_0#Der Vampir
msg_vampire_1#Ich habe vergessen Knoblauch mitzunehmen!
msg_level7_0#Die Tochter des Erzmagiers
msg_level7_1#"Ich komme näher und näher.
msg_level7_2#Sie kann nicht so weit weg sein von hier."
divinity_air_0#Ostram
divinity_air_1#Göttlichkeit von Luft und Sturm.
divinity_air_2#Er mag den Einsatz von Blitz.
div_air_lvl#erhöht deine Geschwindigkeit
div_air_lvl_4#erhöht deinen Blitzangriff
config_joystick#Joystick konfigurieren
redef_joystick#Joystick Steuerung neu einstellen
joystick_configuration#Joystick Konfiguration
joystick_configuration_desc#Bitte den Joystick konfigurieren
joystick_configuration_insert#[Taste drücken]
joystick_not_found#Joystick nicht verbunden!
item_scroll_revelation#Schriftrolle der Offenbarung
item_scroll_revelation_desc#Offenbare Geheimnisse und Trankeffekte
item_potion_01#Roter Trank
item_potion_02#Grüner Trank
item_potion_03#Goldener Trank
item_potion_04#Blauer Trank
item_potion_05#Violetter Trank
item_potion_06#Rosa Trank
item_potion_07#Oranger Trank
item_potion_08#Türkiser Trank
item_potion_09#Brauner Trank
item_potion_10#Dunkler Trank
item_potion_desc#Unbekannter Effekt
item_potion_health#Heiltrank
item_potion_health_desc#Stellt 18 LP wieder her
item_potion_poison#Gifttrank
item_potion_poison_desc#Vergiftet
item_potion_speed#Geschwindigkeitstrank
item_potion_speed_desc#Erhöht temporär die Geschwindigkeit
item_potion_slow#Verlangsamungstrank
item_potion_slow_desc#Verringert temporär die Geschwindigkeit
item_potion_weakness#Schwächetrank
item_potion_weakness_desc#Verringert temporär den Schaden
item_potion_strength#Stärketrank
item_potion_strength_desc#Erhöht temporär den Schaden
item_potion_oblivion#Vergesslichkeitstrank
item_potion_oblivion_desc#Vergiss Trankeffekte und die Karte
item_potion_confusion#Verwirrungstrank
item_potion_confusion_desc#Macht verwirrt
item_potion_cure#Tabula Rasa Trank
item_potion_cure_desc#Bricht alle Spielereffekte ab
item_potion_rage#Wuttrank
item_potion_rage_desc#Wirft Feuerbälle um den Spieler
item_bag#Tasche
item_bag_desc#Liefert mehr Platz für Verbrauchsgüter
msg_potion_id_0#Trank Identifikation
msg_potion_id_1#Du hast identifiziert:
msg_tuto_consumables_0#Tutorial: Verbrauchsgüter
msg_tuto_consumables_1#[num1-num4] um ein Verbrauchsgut zu benutzen
msg_tuto_consumables_2#[left shift][num1-num4] um es fallenzulassen.
msg_tuto_potions_0#Tutorial: Tränke
msg_tuto_potions_1#Du hast einen Trank gefunden.
msg_tuto_potions_2#Du musst ihn trinken oder identifizieren, um den Effekt zu wissen.
secret_found#Geheimraum gefunden!
enemy_type_sausage#eine Wurst
enemy_type_spider_tarantula#eine Tarantel
effect_speed#Geschwindigkeit erhöht!
effect_slow#Geschwindigkeit verringert!
effect_strength#Schaden erhöht!
effect_weakness#Schaden verringert!
effect_confusion#Verwirrt!
effect_forget#Du vergisst!
effect_cure#Tabula rasa!
effect_rage#Wuuuut!
item_book_alchemy#Buch der Alchemie
item_book_alchemy_desc#Offenbart den Effekt einiger Tränke
item_fairy_stone#Fee Cassandra
item_fairy_stone_desc#Hilft dir im Verlies
item_merchant#Gildensiegel des Kaufmanns
item_merchant_desc#Bessere Preise im Shop
item_spell_time#Zauber: Zeitstopp
item_spell_time_desc#Zaubere, um die Zeit zu verlangsamen
item_side_shots#Zauberbuch der Seitwärtsschüsse
item_side_shots_desc#Schießt Schüsse seitlich
item_spell_lightning#Zauber: Blitz
item_spell_lightning_desc#Zaubere, um einen Blitz zu beschwören
item_belt_advanced#Schwarzer Gurt
item_belt_advanced_desc#Erhöht die Feuerrate der Feen
item_rear_shot_advanced#Silberne Personalkappe
item_rear_shot_advanced_desc#Schießt zwei Schüsse rückwärts
item_amulet_retaliation#Amulett der Rache
item_amulet_retaliation_desc#Rächt sich
item_rage_amulet#Wutamulett
item_rage_amulet_desc#Sendet Feuerschüsse
achiev_fairies#Charlie's Engel
achiev_fairies_desc#Bekomme 3 Feen
achiev_ghost#Ghostbuster
achiev_ghost_desc#Töte 50 Geister
achiev_noob#Wahrer Anfänger
achiev_noob_desc#Sterbe in Level 1
achiev_no_damage#Zäher Kerl
achiev_no_damage_desc#Beende 1 Level, ohne ein Leben zu verlieren
achiev_no_damage2#Sehr zäher Kerl
achiev_no_damage2_desc#Beende 2 Level, ohne ein Leben zu verlieren
achiev_no_damage3#Sehr sehr zäher Kerl
achiev_no_damage3_desc#Beende 3 Level, ohne ein Leben zu verlieren
achiev_apostate#Apostat
achiev_apostate_desc#Lehne eine Level 4 Göttlichkeit ab
achiev_fanatic#Fanatiker
achiev_fanatic_desc#Gebe dem Tempel 100 Gold
func_nothing#Nichts!
state_frozen#Eingefroren
state_slow#Langsam
state_speed#Eilig
state_poison#Vergiftet
state_weakness#Schwäche
state_strength#Stärke
state_rage#Wut
state_confusion#Verwirrt
state_time#Zeitstopp
state_div_protection#Geschützt (div)
state_div_speed#Eilig (div)
state_div_firerate#Feuerrate+ (div)
state_div_damage#Schaden+ (div)
dc_victory#Siegeszertifikat
dc_after_victory#Sieg danach
player#Spieler
players#Spieler
ui_base_damage#Basisschaden
ui_fire_rate#Feuerrate
ui_second#Sekunde
ui_temple_donation#Tempelspende
[spanish]
start_new_game#Empezar nueva partida
start_desc#La partida anterior se perderá
restore#Restaurar partida
at#en
level#nivel
begin_journey#Comienza tu aventura en un nuevo calabozo
config_keys#Configurar teclas
redef_input#Redefinir controles del jugador
exit_game#Salir del juego
return_to_desktop#Volver al escritorio
key_configuration#Configuración de teclas
key_configuration_desc#Por favor configure las teclas
key_configuration_insert#[presione tecla]
key_configuration_done#LISTO
key_move_up#moverse arriba
key_move_down#moverse abajo
key_move_left#moverse a la izquierda
key_move_right#moverse a la derecha
key_fire_up#disparar arriba
key_fire_down#disparar abajo
key_fire_left#disparar a la izquierda
key_fire_right#disparar a la derecha
key_fire_select#seleccionar disparo
key_time#control del tiempo
key_fire#disparo (un botón)
configure_game#Configuración
configure_game_desc#Cambia las opciones del juego
config_back#Volver
config_back_desc#Volver al menú principal
config_lang#Idioma
config_lang_desc#Elije usando las teclas izquierda y derecha
english#Inglés
french#Francés
german#Alemán
spanish#Español
russian#Ruso
keys_move#mover
keys_fire#disparar
keys_select_1#disparo
keys_select_2#selector
keys_time#control de tiempo
key_spell#invocar un conjuro
play_again#Presiona [ENTER] para jugar de nuevo / [ESC] para volver al menú
items_found#Items
monsters_killed#Monstruos
congratulations_1#FELICIDADES !
congratulations_2#Te haz enfrentado a este demo y has derrotado a los jefes!
congratulations_3#Nos veremos luego en una nueva aventura !
by#por
critical#CRÍTICO
poison#VENENO
merchant_speech_0#¡Los mejores precios de todo el calabozo!
merchant_speech_1#¡Bienvenido a la tienda Pawn'n'Witches!
merchant_speech_2#¡Echa una mirada a nuestra mercancía!
enemy_butcher#Cerdo Carnicero
enemy_giant_slime#Baba Gigante
enemy_cyclops#Cíclope de piedra
enemy_rat_king#Hombre Rata Rey
enemy_giant_spider#Araña gigante
item_copper#Moneda de cobre
item_copper_desc#Una moneda de cobre (valor 1)
item_silver#Moneda de plata
item_silver_desc#Una moneda de plata (valor 5)
item_gold#Moneda de oro
item_gold_desc#Una moneda de oro (valor 10)
item_titan_heart#Corazón de Titán
item_titan_heart_desc#Incrementa PV máximos
item_elven_cookie#Galleta Elven
item_elven_cookie_desc#Restaura y aumenta PV máximos
item_apple#Manzana
item_apple_desc#Restaura 3 PV
item_bread#Pan
item_bread_desc#Restaura 7 PV
item_cheese#Queso
item_cheese_desc#Restaura 15 PV
item_boss_key#Llave del jefe
item_boss_key_desc#Abre la puerta del jefe
item_sorcerer_hat#Sombrero del hechicero
item_sorcerer_hat_desc#Aumenta velocidad de disparo
item_velvet_boots#Botas de terciopelo
item_velvet_boots_desc#Aumenta la velocidad
item_spell_dual#Conjuro : Rayo doble
item_spell_dual_desc#Dispara dos rayos
item_displacement_gloves#Guantes de desplazamiento
item_displacement_gloves_desc#La velocidad del jugador afecta la velocidad del proyectil
item_staff#Báculo Ébano
item_staff_desc#Aumenta la velocidad y el daño del rayo
item_fairy#Hada Lilly
item_fairy_desc#Te ayuda en el calabozo
item_leather_belt#Cinturón de cuero
item_leather_belt_desc#Aumenta la velocidad de disparo
item_blood_snake#Serpiente de sangre
item_blood_snake_desc#Aumenta el daño
item_gem_ice#Gema de hielo
item_gem_ice_desc#Ataque de hielo (congelar)
item_gem_illusion#Gema de ilusión
item_gem_illusion_desc#Ataque de ilusión (ignora paredes y armaduras)
item_gem_stone#Gema de piedra
item_gem_stone_desc#Ataque de piedra (repeler)
item_gem_lightning#Gema de trueno
item_gem_lightning_desc#Ataque de trueno (rebote)
item_fairy_ice#Hada Natasha
item_fairy_ice_desc#Te ayuda en el calabozo
item_ring_ice#Anillo de safiro
item_ring_ice_desc#Aumenta el poder del hielo
item_ring_stone#Anillo de cuarso
item_ring_stone_desc#Aumenta el poder de la piedra
item_ring_lightning#Anillo de amatista
item_ring_lightning_desc#Aumenta el poder del trueno
item_ring_illusion#Anillo de obsidiana
item_ring_illusion_desc#Aumenta el poder de la ilusión
item_spell_triple#Conjuro : Rayo triple
item_spell_triple_desc#Dispara tres rayos
item_brooch_finesse#Barba falsa de archimago
item_brooch_finesse_desc#Probabilidad de disparo crítico (x2)
item_fairy_fire#Hada Alicia
item_fairy_fire_desc#Te ayuda en el calabozo
item_fairy_target#Hada Scarlett
item_fairy_target_desc#Te ayuda en el calabozo
item_robe_magician#Bata del Mago
item_robe_magician_desc#Aumenta armadura
item_gem_fire#Gema de fuego
item_gem_fire_desc#Ataque de fuego (más daño)
item_ring_fire#Anillo de rubí
item_ring_fire_desc#Aumenta el poder del fuego
item_manual_staff#Manual del báculo
item_manual_staff_desc#Aumenta rango de disparo
item_spell_teleport#Conjuro: Teletransportación
item_spell_teleport_desc#Invoca teletransportarse dentro de la habitación
item_spell_slime_explode#Conjuro: Invocar baba
item_spell_slime_explode_desc#Invoca una baba explosiva
spell_teleport#Teletransportación
spell_slime_explode#Invocar baba
spell_fireball#bola de fuego
spell_freeze#congelar monstruos
spell_earthquake#terremoto
spell_protection#escudo mágico
item_spell_fireball#Conjuro: Bola de fuego
item_spell_fireball_desc#Invocar para lanzar una bola de fuego
item_spell_freeze#Conjuro: Congelar monstruos.
item_spell_freeze_desc#Invocar para congelar a los monstruos.
item_spell_earthquake#Conjuro: Terremoto
item_spell_earthquake_desc#Invocar para hacer que el calabozo tiemble
item_spell_protection#Conjuro: Escudo mágico
item_spell_protection_desc#Invocar para crear un escudo mágico
msg_level1_0#Tu expedición comienza!
msg_level1_1#"Brujos han abducido a la hija del Archimago y la han llevado a la guarida.
msg_level1_2#Yo debo rescatarla!"
msg_level2_0#La hija del Archimago
msg_level2_1#"¡Voy ahora mismo a rescatarte!
msg_level2_2#Esperemos que ella esté sana y salva..."
msg_level3_0#La hija del Archimago
msg_level3_1#"¿No te lo he contado?
msg_level3_2#La hija del Archimago es la mujer más dulce de la Academia de Magia!"
msg_level4_0#El Archimago
msg_level4_1#"El Archimago es un hombre muy sabio, y además es mi mentor.
msg_level4_2#Él me ha enseñado que vivimos en lo mejor del mundo."
msg_level5_0#La hija del Archimago
msg_level5_1#"Recuerdo a la hija del Archimago conmigo, de vuelta en la Academia de Magia.
msg_level5_2#Solíamos jugar cerca del mago sanador."
msg_level6_0#El Archimago
msg_level6_1#"Mis padres fallecieron cuando yo era un bebé.
msg_level6_2#El Archimago me crió como si fuera su propio hijo."
msg_rats_bats_0#Monstruos
msg_rats_bats_1#"¡Oye! ¿Por qué siempre tenemos que matar ratas
msg_rats_bats_2#o murciélagos en estos juegos tipo Rogue?"
msg_snakes_0#Serpientes
msg_snakes_1#"ODIO a las serpientes!"
msg_witches_0#Brujas
msg_witches_1#"Pu... Brujas !!!"
msg_gold_0#Oro
msg_gold_1#"♫ Money money money ♫
msg_gold_2#Me pregunto si puedo comprar cosas útiles en este calabozo..."
msg_familiar_0#Hadas
msg_familiar_1#"¡Oh, tengo compañía!
msg_familiar_2#Ojalá encuentre a más aliados en este calabozo."
msg_butcher_0#El Cerdo Carnicero
msg_butcher_1#"¡Wow, este tipo si que es grandote!
msg_butcher_2#Espero no terminar hecho salchichas..."
msg_giant_slime_0#La Baba Gigante
msg_giant_slime_1#"Soy verde, grande y apesto.
msg_giant_slime_2#¿Qué soy?"
msg_cyclops_0#El Cíclope de piedra
msg_cyclops_1#"¿Cómo se pudo meter en la habitación?
msg_cyclops_2#¡Es más grande que la puerta!"
msg_wererat_0#El Hombre Rata
msg_wererat_1#"Se parece a mi vieja tía..."
msg_giant_spider_before_0#La habitación vacía
msg_giant_spider_before_1#"¿Hay alguien ahí? "
msg_giant_spider_after_0#La Araña Gigante
msg_giant_spider_after_1#"ODIO a las arañas"
msg_tuto_intro_0#Tutorial: Cosas básicas
msg_tuto_intro_1#Usa WASD (por defecto) para moverse dentr del calabozo.
msg_tuto_intro_2#Dispara usando las flechas (por defecto).
msg_tuto_items_0#Tutorial: Ítems
msg_tuto_items_1#Ítems mejoran a tu personaje o te dan nuevos poderes.
msg_tuto_items_2#Encuentra o compra la mayor cantidad que puedas.
msg_tuto_heal_0#Tutorial: Puntos de vida
msg_tuto_heal_1#Haz perdido algunos puntos de vida.
msg_tuto_heal_2#Encuentra algo para curarte antes de que mueras.
msg_tuto_shops_0#Tutorial: Tiendas
msg_tuto_shops_1#Puedes encontrar varias tiendas en el calabozo.
msg_tuto_shops_2#Aquí puedes gastar dinero para comprar ítems o comida.
msg_tuto_boss_door_0#Tutorial: Puerta del Jefe
msg_tuto_boss_door_1#Desde aquí puedes ver la puerta dorada cerrada.
msg_tuto_boss_door_2#Esa es la habitación del jefe, pero necesitas la llave dorada para entrar.
msg_tuto_challenge_door_0#Tutorial: Habitación de Desafíos
msg_tuto_challenge_door_1#Mira la puerta decorada. Es una habitación de desafíos.
msg_tuto_challenge_door_2#Puedes obtener buenas recompenzas ahí, ¡Pero entra con precaución!
msg_tuto_shots_0#Tutorial: Tipos de disparo
msg_tuto_shots_1#Haz encontrado un nuevo tipo de disparo.
msg_tuto_shots_2#Presiona la tecla de seleccionar disparos (por defecto: Tab) para cambiar entre tipos de disparo.
msg_tuto_spell_0#Tutorial: Conjuros
msg_tuto_spell_1#Haz encontrado un conjuro mágico.
msg_tuto_spell_2#Presiona la tecla de conjuros (por defecto: Espacio) para realizar el conjuro.
enemy_type_bat#un murciélago
enemy_type_rat#una rata
enemy_type_rat_black#una rata negra
enemy_type_rat_helmet#una rata con casco
enemy_type_rat_black_helmet#una rata negra con casco
enemy_type_evil_flower#una flor malvada
enemy_type_evil_flower_ice#una flor de las nieves malvada
enemy_type_snake#una serpiente
enemy_type_snake_blood#una serpiente ensangrentada
enemy_type_slime#una baba
enemy_type_slime_red#una baba roja
enemy_type_slime_blue#una baba azul
enemy_type_slime_violet#una baba violeta
enemy_type_imp_blue#un diablillo azul
enemy_type_imp_red#un diablillo rojo
enemy_type_pumpkin#una calabaza loca
enemy_type_witch#una bruja
enemy_type_witch_red#una bruja roja
enemy_type_cauldron#un caldero
enemy_type_bubble#una burbuja
enemy_type_boss_butcher#el Cerdo Carnicero
enemy_type_boss_slime_giant#la Baba Gigante
enemy_type_boss_cyclops#el Cíclope de piedra.
enemy_type_boss_rat_king#el Rey Hombre-Rata
enemy_type_boss_spider_giant#la Araña Gigante
enemy_type_green_rat#una rata verde
enemy_type_rock_falling#una roca cayendo
enemy_type_rock_missile#una roca lanzable
enemy_type_spider_egg#un huevo de araña cayendo
enemy_type_spider_little#una araña
enemy_type_spider_web#una tela-araña
enemy_type_ghost#un fantasma
enemy_type_zombie#un zombie
enemy_type_zombie_dark#un zombie oscuro
enemy_type_francky#la Cosa
enemy_type_himself#él mismo
source_explosion#explosión
source_poison#veneno
enemy_francky#La Cosa
enemy_francky_head#La cabeza de La Cosa
item_apple_green#Manzana verde
item_apple_green_desc#Restaura 3 PV y cura el veneno
item_manual_slime#Manual de Maestría de Baba
item_manual_slime_desc#Las babas ya no te atacan directamente
item_spell_dual_quick#El libro de los rayos rápidos dobles
item_spell_dual_quick_desc#Dispara dos rayos (rápido)
item_spell_triple_quick#El libro de los rayos rápidos triples
item_spell_triple_quick_desc#Dispara tres rayos (rápido)
item_manual_health#Manual de Salud
item_manual_health_desc#Los objetos de salud dan más PV
item_rear_shot#Gorra del váculo
item_rear_shot_desc#Dispara un rayo hacia trás
item_book_random_shot#Libro del disparo al azar
item_book_random_shot_desc#Dispara un rayo en una dirección al azar
item_spell_web#Conjuro: Telaraña
item_spell_web_desc#Invocar para lanzar telarañas
item_book_magic_i#Libro de magia - Tomo I
item_book_magic_i_desc#Disminuye el tiempo de espera de los conjuros
item_book_magic_ii#Libro de magia - Tomo II
item_book_magic_ii_desc#Aumenta el poder de los conjuros
item_sulfur#Sulfuro
item_sulfur_desc#Puede hacer que los cadáveres exploten
spell_web#telaraña
tuto_reset#Resetear tutorial
tuto_reset_desc#Resetea el tutorial
msg_francky_0#La... Cosa
msg_francky_1#Yuck... ¡Parece como si estuviera hecho con partes de distintos cuerpos!
msg_tuto_tips_0#Tutorial: Consejos
msg_tuto_tips_1#Usa el Control del Tiempo (por defecto: RShift) para viajar rápidamente.
msg_tuto_tips_2#Presiona [Enter] para cerrar el cuadro de mensajes inmediatamente.
menu_continue#Continuar
menu_continue_desc#Continúa con el juego
menu_save_quit#Guardar y salir
menu_save_quit_desc#Guarda la partida y se vuelve al menú principal
menu_quit#Salir sin guardar
menu_quit_desc#Vuelve al menú principal sin guardar la partida
inventory#Inventario
dc_certificate#Certificado de defunción
dc_killed_by#Matado por
dc_died_level#Muerto en nivel
dc_after#tras
dc_minutes#mins. de juego
dc_killed_monsters#Monstruos asesinados
dc_gold#Oro
dc_challenges#Desafíos completados
player_name#Nombre del jugador
player_name_desc#Modifica el nombre del jugador
hi_scores#Salón de la fama
hi_scores_desc#Mejores puntajes
credits#Créditos
credits_desc#Pantalla de créditos
volume_sound#Volumen del sonido
volume_sound_desc#Usa las flechas izquierda/derecha para cambiar el volumen
volume_music#Volumen de la música
certificate_capture#Presione [F1] para guardar el certificado en un archivo PNG
certificate_saved#Guardado en
msg_tuto_temple_0#Tutorial: Templo
msg_tuto_temple_1#El templo es el lugar en donde puedes venerar a una divinidad o donar.
msg_tuto_temple_2#Las Divinidades te pueden ayudar y te dan bonificaciones.
key_interact#interactuar
interact_worship#Venerar
interact_shop#Comprar
interact_donate#Donar 10 de oro
interact_donate_fail#(no hay suficiente oro)
worshipping#Ahora eres un venerador de
divinity_healer_0#Pomonos el Honrado
divinity_healer_1#Guardián de la honradez and patrocinador de los débiles.
divinity_healer_2#Él aprecia las donaciones, el sufrimiento y la lucha contra el mal.
divinity_fighter_0#Tello el Poderoso
divinity_fighter_1#Señor de los guerreros y los fuertes.
divinity_fighter_2#Él aprecia monstruos y titanes cayendo en la batalla.
divinity_ice_0#Señor Nieve
divinity_ice_1#Divinidad de la frialdad.
divinity_ice_2#Él aprecia el uso del hielo.
divinity_stone_0#Gaya
divinity_stone_1#Divinidad de la piedra y de la tierra.
divinity_stone_2#Él aprecia el uso de la piedra.
divinity_intervention_0#Intervención divina.
divinity_intervention_1#interviene a tu favor.
divinity_gift_0#Regalo divino.
divinity_gift_1#te concede un regalo.
item_floor_map#Mapa del calabozo
item_floor_map_desc#Revela el mapa del piso actual.
item_alcohol#Absenta
item_alcohol_desc#aumenta los daños temporalmente.
achiev_rats#Roedoricidio
achiev_rats_desc#Mata 250 ratas
achiev_100#Tacaño
achiev_100_desc#Recoleta 100 monedas de oro
achiev_piety_max#Fiel
achiev_piety_max_desc#Obtén el máximo nivel de devoción
achiev_4_hits#Brocheta
achiev_4_hits_desc#Daña a 4 monstruos con el mismo disparo
achiev_complete_set#Vestido
achiev_complete_set_desc#Equipar bata + sombrero + botas +guantes
achiev_giant_slime#Cazador de Babas Gigantes
achiev_giant_slime_desc#Mata a la Baba Gigante
achiev_cyclops#Cazador de Cíclopes
achiev_cyclops_desc#Mata al Cíclope de Piedra
achiev_rat_king#Cazador de Hombres-Rata
achiev_rat_king_desc#Mata al Rey Hombre-Rata
achiev_giant_spider#Cazador de Arañas Gigantes
achiev_giant_spider_desc#Mata a la Araña Gigante
achiev_francky#Cazador de Cosas
achiev_francky_desc#Mata a la Cosa
achiev_butcher#Cazador de Carniceros
achiev_butcher_desc#Mata al Carnicero
achiev_witches#Inquisidor
achiev_witches_desc#Mata a 50 brujas
achiev_eggs#Mala tortilla
achiev_eggs_desc#Destruye 100 huevos
achiev_vampire#Caza-Vampiros
achiev_vampire_desc#Mata al Vampiro
achiev_challenges#Gladiador
achiev_challenges_desc#Completa 4 desafíos
achiev_win#Vivir el sueño
achiev_win_desc#Completa el demo
func_achiev_screen#Pantalla de logros
func_achiev_all#Todas las descripciones de los logros
func_double_spell#Dos encantos aparecen
enemy_type_evil_flower_fire#una flor de fuego malvada
enemy_type_bat_skeleton#un esquéleto murciélago
enemy_type_slime_large#una baba grande
enemy_type_slime_blue_large#una baba azul grande
enemy_type_slime_red_large#una baba roja grande
enemy_type_slime_violet_large#una baba violeta grande
enemy_type_bogeyman#un hombre del saco
enemy_type_cauldron_elemental#una caldera elemental
menu_achievements#Logros
menu_achievements_desc#Muestra los logros
achievement_complete#¡Logro desbloqueado!
item_spell_flower#Encanto:Invocar flor mágica
item_spell_flower_desc#Usar para invocar a flor mágica
item_rapid_shots#Libro de disparo rápido
item_rapid_shots_desc#Ametralladora de hechiceros
item_fairy_poison#Hada Vicky
item_fairy_poison_desc#Te ayuda en el calabozo
item_luck#Trébol de cuatro hojas
item_luck_desc#Aumenta temporalmente el oro encontrado
item_fairy_powder#Polvo de hadas
item_fairy_powder_desc#Exalta temporalmente a las hadas
item_ring_poison#Anillo de esmeralda
item_ring_poison_desc#Aumenta el poder de envenenamiento
item_gem_poison#Gema de veneno
item_gem_poison_desc#Ataque de veneno
item_book_prayer_i#Libro de rezos - Tomo I
item_book_prayer_i_desc#Incrementa la devoción
item_book_prayer_ii#Libro de rezos - Tomo II
item_book_prayer_ii_desc#Reduce el costo de la devoción
item_pet_slime#Baba mascota
item_pet_slime_desc#Te seguirá y ayudará
item_hat_advanced#Gorra de Warlock
item_hat_advanced_desc#Resistencia elemental y aumenta velocidad de ataque
item_robe_advanced#Bata mágica de choque
item_robe_advanced_desc#Repulsa e incrementa la armadura
item_critical#Barba de mago falsa
item_critical_desc#Probabilidad de ataque crítico (x2)
item_critical_advanced#Barba de archimago falsa
item_critical_advanced_desc#Probabilidad de ataque crítico (x3)
item_spell_fairy#Encanto: Transmutación de hadas
item_spell_fairy_desc#Usar para transmutar una hada
item_gloves_advanced#Guantes de guardabosques
item_gloves_advanced_desc#Desarma trampas y aumenta velocidad de ataque
item_boots_advanced#Botas de guardabosques
item_boots_advanced_desc#Aumenta velocidad
divinity_leveling_0#¡Mayor nivel de devoción!
div_fighter_lvl#Incrementa tu daño
div_healer_lvl_2#Te da regeneración
div_healer_lvl_3#Aumenta tu regeneración
div_ice_lvl_3#Incrementa tu resistencia al hielo
div_ice_lvl_4#Incrementa tu ataque de hielo
div_ice_lvl_5#Incrementa tu inmunidad al hielo
div_stone_lvl_3#Incrementa tu resistencia a la piedra
div_stone_lvl_4#Incrementa tu ataque de piedra
div_stone_lvl_5#Incrementa tu repulsión a la piedra
enemy_vampire#Vampiro
enemy_type_vampire#El Vampiro
msg_tuto_achiev_0#Tutorial: Logros
msg_tuto_achiev_1#Haz completado un logro-
msg_tuto_achiev_2#Desbloquean nuevos ítems o bonificaciones.
msg_vampire_0#El Vampiro
msg_vampire_1#¡Olvidé traer algo de ajo!
msg_level7_0#La hija del Archimago
msg_level7_1#"Me acerco más y más.
msg_level7_2#Ella no debería estar tan lejos de aquí."
divinity_air_0#Ostram
divinity_air_1#Divinidad del aire y la tormenta
divinity_air_2#Le gusta el uso de la electricidad.
div_air_lvl#Incrementa tu velocidad
div_air_lvl_4#increases your ataque eléctrico.
config_joystick#Configurar joystick
redef_joystick#Redefinir controles joystick
joystick_configuration#Configuración joystick
joystick_configuration_desc#Por favor configurar joystick
joystick_configuration_insert#[presionar botón]
joystick_not_found#¡Joystick no conectado!
item_scroll_revelation#Perfamino de revelaciones
item_scroll_revelation_desc#Revela secretos y efectos de pociones
item_potion_01#Poción roja
item_potion_02#Poción verde
item_potion_03#Poción dorada
item_potion_04#Poción azul
item_potion_05#Poción violeta
item_potion_06#Poción rosa
item_potion_07#Poción naranja
item_potion_08#Poción turquesa
item_potion_09#Poción café
item_potion_10#Poción oscura
item_potion_desc#Efecto desconocido
item_potion_health#Poción de curación
item_potion_health_desc#Restaura 18 PV
item_potion_poison#Poción de veneno
item_potion_poison_desc#Envenena
item_potion_speed#Poción de velocidad
item_potion_speed_desc#Aumenta al velocidad temporalmente
item_potion_slow#Poción lenta
item_potion_slow_desc#Disminuye la velocidad temporalmente
item_potion_weakness#Poción de debilidad
item_potion_weakness_desc#Disminuye el daño temporalmente
item_potion_strength#Poción de fuerza
item_potion_strength_desc#Aumenta el daño temporalmente
item_potion_oblivion#Poción del olvido
item_potion_oblivion_desc#Olvida el mapa y los efectos de poción
item_potion_confusion#Poción de confusión
item_potion_confusion_desc#Te vuelve confuso
item_potion_cure#Poción de Tábula Rasa
item_potion_cure_desc#Cancela todos los efectos del jugador
item_potion_rage#Poción de rabia
item_potion_rage_desc#Lanza bolas de fuego alrededor del jugador
item_bag#Bolsa
item_bag_desc#Da más espacio para objetos consumibles
msg_potion_id_0#Identificación de poción
msg_potion_id_1#Haz identificado:
msg_tuto_consumables_0#Tutorial:Consumibles
msg_tuto_consumables_1#[num1-num4] para usar items consumibles.
msg_tuto_consumables_2#[left shift][num1-num4] para botarlos.
msg_tuto_potions_0#Tutorial:Pociones
msg_tuto_potions_1#Haz encontrado una poción.
msg_tuto_potions_2#Necesitas beberla o identificarla para conocer su efecto.
secret_found#¡Habitación secreta encontrada!
enemy_type_sausage#una salchicha
enemy_type_spider_tarantula#una tarántula
effect_speed#¡Más velocidad!
effect_slow#¡Menor velocidad!
effect_strength#¡Más daño!
effect_weakness#¡Menos daño!
effect_confusion#¡Confundido!
effect_forget#¡Olvidadiso!
effect_cure#¡Tábula rasa!
effect_rage#¡Raaabiaaaa!
item_book_alchemy#Libro de alquimia
item_book_alchemy_desc#Revela los efectos de algunas pociones
item_fairy_stone#Hada Cassandra
item_fairy_stone_desc#Te ayuda en el calabozo
item_merchant#Sello del mercantil
item_merchant_desc#Mejores precios en la tienda
item_spell_time#Encanto:Detener tiempo
item_spell_time_desc#Invocar para hacer el tiempo más lento
item_side_shots#Libro de disparos laterales
item_side_shots_desc#Dispara rayos laterales
item_spell_lightning#Encanto: Relámpago
item_spell_lightning_desc#Invocar para lanzar relámpagos
item_belt_advanced#Cinturón negro
item_belt_advanced_desc#Aumenta el tiempo de disparo de las hadas
item_rear_shot_advanced#Báculo plateado
item_rear_shot_advanced_desc#Dispara dos disparos hacia atrás
item_amulet_retaliation#Amuleto de venganza
item_amulet_retaliation_desc#Contraataca
item_rage_amulet#Amuleto de furia
item_rage_amulet_desc#Lanza disparos de fuego
achiev_fairies#Los Ángles de Charlie
achiev_fairies_desc#Obtén 3 hadas
achiev_ghost#Cazafantasma
achiev_ghost_desc#Mata 50 fantasmas
achiev_noob#Novato de verdad
achiev_noob_desc#Muere en el primer nivel
achiev_no_damage#Tipo rudo
achiev_no_damage_desc#Completa un nivel sin perder vida
achiev_no_damage2#Tipo muy rudo
achiev_no_damage2_desc#Completa dos niveles sin perder vida
achiev_no_damage3#Tipo muy muy rudo
achiev_no_damage3_desc#Completa tres niveles sin perder vida
achiev_apostate#Apóstata
achiev_apostate_desc#Rechaza una divinidad nivel 4
achiev_fanatic#Fanático
achiev_fanatic_desc#Da 100 de oro al templo
func_nothing#¡Nada!
state_frozen#Congelado
state_slow#Lento
state_speed#Apurado
state_poison#Envenenado
state_weakness#Debilidad
state_strength#Fuerza
state_rage#Rabia
state_confusion#Confuso
state_time#Detener tiempo
state_div_protection#Protegido (div)
state_div_speed#Apurado (div)
state_div_firerate#Tiempo de disparo+ (div)
state_div_damage#Daño+ (div)
dc_victory#Certificado de victoria
dc_after_victory#Victoria después
player#jugador
players#jugadores
ui_base_damage#Daño base
ui_fire_rate#Velocidad de disparo
ui_second#segundo
ui_temple_donation#Donación al Templo
[russian]
start_new_game#Начать новую игру
start_desc#Старая игра будет стёрта
restore#Продолжить игру
at#в
level#Уровень
begin_journey#Начните ваше приключение в новом подземелье
config_keys#Настройка управления
redef_input#Сконфигурируйте управление игры
exit_game#Выход из игры
return_to_desktop#Выйти в ОС
key_configuration#Конфигурация клавиш
key_configuration_desc#Пожалуйста, задайте управление
key_configuration_insert#[нажмите клавишу]
key_configuration_done#ГОТОВО
key_move_up#Движение вверх
key_move_down#Движение вниз
key_move_left#Движение влево
key_move_right#Движение вправо
key_fire_up#Выстрел вверх
key_fire_down#Выстрел вниз
key_fire_left#Выстрел влево
key_fire_right#Выстрел вправо
key_fire_select#Выбор оружия
key_time#Ускорение времени
key_fire#Выстрел в направлении движения
key_spell#Произнести заклинание
configure_game#Конфигурация
configure_game_desc#Изменение параметров игры
config_back#Назад
config_back_desc#Назад в главное меню
config_lang#Язык
config_lang_desc#Выберите с помощью клавиш влево и вправо
russian#Русский
keys_move#движение
keys_fire#выстрел
keys_select_1#выбор
keys_select_2#оружия
keys_time#ускорение
play_again#Нажмите [ENTER] чтобы сыграть снова / [ESC] чтобы выйти в меню
items_found#Предметы
monsters_killed#Монстры
congratulations_1#ПОЗДРАВЛЯЕМ !
congratulations_2#Вы прошли демо и победили всех боссов !
congratulations_3#Скоро увидимся с новыми приключениями !
by#
critical#КРИТИЧЕСКИЙ УДАР
poison#ЯД
merchant_speech_0#Лучшие цены во всем подземелье!
merchant_speech_1#Добро пожаловать в магазин "Ведьмы и пешки"!
merchant_speech_2#Да вы только взгляните на наш товар!
enemy_butcher#Свин-мясник
enemy_giant_slime#Гигантский слизень
enemy_cyclops#Каменный циклоп
enemy_rat_king#Король веркрыс
enemy_giant_spider#Гигантский паук
item_copper#Медная монета
item_copper_desc#Медная монета (ценность 1)
item_silver#Серебряная монета
item_silver_desc#Серебряная монета (ценность 5)
item_gold#Золотая монета
item_gold_desc#Золотая монета (ценность 10)
item_titan_heart#Сердце титана
item_titan_heart_desc#Увеличивает максимальный уровень HP
item_elven_cookie#Печенье эльфов
item_elven_cookie_desc#Восстанавливает HP и увеличивает его максимальный уровень
item_apple#Яблоко
item_apple_desc#Восстанавливает 3 HP
item_bread#Хлеб
item_bread_desc#Восстанавливает 7 HP
item_cheese#Сыр
item_cheese_desc#Восстанавливает 15 HP
item_boss_key#Ключ Босса
item_boss_key_desc#Открывает врата в комнату Босса
item_sorcerer_hat#Шляпа мага
item_sorcerer_hat_desc#Увеличивает скорость стрельбы
item_velvet_boots#Бархатные сапоги
item_velvet_boots_desc#Увеличивает скорость
item_spell_dual#Заклинание : Двойной выстрел
item_spell_dual_desc#Позволяет стрелять двойным снарядом
item_rage_amulet#Амулет ярости
item_rage_amulet_desc#Месть
item_displacement_gloves#Перчатки перемещения
item_displacement_gloves_desc#Скорость персонажа влияет на скорость снарядов
item_staff#Эбонитовый посох
item_staff_desc#Увеличивает скорость снаряда и урон
item_fairy#Фея Лилли
item_fairy_desc#Помогает вам в подземелье
item_leather_belt#Кожаный ремень
item_leather_belt_desc#Увеличивает скорость стрельбы
item_blood_snake#Кровавый змей
item_blood_snake_desc#Увеличивает урон
item_gem_ice#Лёд
item_gem_ice_desc#Ледяная атака (заморозка)
item_gem_illusion#Камень иллюзий
item_gem_illusion_desc#Иллюзтонная атака (Игнорирует стены и броню)
item_gem_stone#Камень
item_gem_stone_desc#Каменная атака (отброс)
item_gem_lightning#Камень молний
item_gem_lightning_desc#Атака молний (отражение)
item_fairy_ice#Фея Наташа
item_fairy_ice_desc#Помогает вам в подземелье
item_ring_ice#Кольцо с сапфиром
item_ring_ice_desc#Увеличивает силу льда
item_ring_stone#Кольцо с кварцем
item_ring_stone_desc#Увеличивает силу камня
item_ring_lightning#Кольцо с аметистом
item_ring_lightning_desc#Увеличивает силу молний
item_ring_illusion#Кольцо с обсидианом
item_ring_illusion_desc#Увеличивает силу иллюзий
item_spell_triple#Заклинание : Тройной выстрел
item_spell_triple_desc#Позволяет стрелять тройным снарядом
item_brooch_finesse#Фальшивая борода архимага
item_brooch_finesse_desc#Шанс критического попадания (x2)
item_fairy_fire#Фея Алиса
item_fairy_fire_desc#Помогает вам в подземелье
item_fairy_target#Фея Скарлет
item_fairy_target_desc#Помогает вам в подземелье
item_robe_magician#Роба волшебника
item_robe_magician_desc#Увеличивает броню
item_gem_fire#Камень огня
item_gem_fire_desc#Атака огня (увеличенный урон)
item_ring_fire#Кольцо с рубином
item_ring_fire_desc#Увеличивает силу огня
item_manual_staff#Руководство к посоху
item_manual_staff_desc#Увеличивает дальность стрельбы
item_spell_teleport#Заклинание: Телепорт
item_spell_teleport_desc#Произнесите, чтобы телепортироваться в любое место комнаты
item_spell_slime_explode#Spell: Призыв слизня
item_spell_slime_explode_desc#Произнесите, чтобы призвать взрывного слизня
item_spell_fireball#Заклинание: Огненный шар
item_spell_fireball_desc#Произнесите, чтобы вызвать огненный шар
item_spell_freeze#Заклинание: Заморозка монстров
item_spell_freeze_desc#Произнесите, чтобы заморозить монстров
item_spell_earthquake#Заклинание: землетрясение
item_spell_earthquake_desc#Произнесите, чтобы сотрясти все подземелье
item_spell_protection#Заклинание: Магический щит
item_spell_protection_desc#Произнесите, чтобы призвать магический щит
spell_teleport#Телепорт
spell_slime_explode#Призвать слизня
spell_fireball#огненный шар
spell_freeze#заморозка монстров
spell_earthquake#землетрясение
spell_protection#магический щит
msg_level1_0#Ваше приключение началось!
msg_level1_1#"Ведьмы похитили дочь архимага и утащили ее в свое логово.
msg_level1_2#Мне нужно ее вернуть!"
msg_level2_0#Дочь архимага
msg_level2_1#"Я пришел чтобы спасти тебя!
msg_level2_2#Надеюсь, она жива..."
msg_level3_0#Дочь архимага
msg_level3_1#"Я когда-нибудь рассказывал тебе?
msg_level3_2#Дочь архимага - приятнейшая девушка во всей магической академии!"
msg_level4_0#Архимаг
msg_level4_1#"Архимаг - очень мудрый человек... А так же мой наставник.
msg_level4_2#Он показал мне, что мы живем в лучшем из миров."
msg_level5_0#Дочь архимага
msg_level5_1#"Я помню нас с дочерью архимага в магической академии.
msg_level5_2#Мы игрались с магией лечения, хи-хи !"
msg_level6_0#Архимаг
msg_level6_1#"Мои родители погибли, когда я был совсем крохой.
msg_level6_2#И архимаг растил меня как своего собственного сына."
msg_rats_bats_0#Монстры
msg_rats_bats_1#"Эй! Почему мы постоянно должны истреблять крыс и летучих мышей
msg_rats_bats_2#в рогаликах ?"
msg_snakes_0#Змеи
msg_snakes_1#"Я НЕНАВИЖУ ЗМЕЙ!"
msg_witches_0#Ведьмы
msg_witches_1#"Бл... Ведьмы !!!"
msg_gold_0#Золото
msg_gold_1#"♫ Money money money ♫
msg_gold_2#Я удивлюсь, если смогу купить хоть что-то полезное в этом подземелье..."
msg_familiar_0#Феи
msg_familiar_1#"Ох! Наконец-то я буду не один!
msg_familiar_2#Надеюсь, мне удастся найти больше союзников в этом подземелье."
msg_butcher_0#Свиночел-мясник
msg_butcher_1#"Эй, смотри какой здоровяк!
msg_butcher_2#Надеюсь, удастся порубить его на колбасу..."
msg_giant_slime_0#Гигантский слизень
msg_giant_slime_1#"Я зеленый, большой... И я воняю.
msg_giant_slime_2#Что же я такое?"
msg_cyclops_0#Каменный циклоп
msg_cyclops_1#"Как он прошел в комнату?
msg_cyclops_2#Он же гораздо больше двери!"
msg_wererat_0#Король веркрыс
msg_wererat_1#"Выглядит как моя старая тетка..."
msg_giant_spider_before_0#Пустая комната
msg_giant_spider_before_1#"Здесь есть кто-нибудь? "
msg_giant_spider_after_0#Гигантский паук
msg_giant_spider_after_1#"Я НЕНАВИЖУ ПАУКОВ"
msg_tuto_intro_0#Обучение: Основы
msg_tuto_intro_1#Используйте WASD (по умолчанию) для перемещения по подземелью.
msg_tuto_intro_2#Стреляйте клавишами со стрелочками (по умолчанию).
msg_tuto_items_0#Обучение: Предметы
msg_tuto_items_1#Предметы предоставляют вашему персонажу больше возможностей.
msg_tuto_items_2#Найдите или купите столько, сколько сможете.
msg_tuto_heal_0#Обучение: Здоровье
msg_tuto_heal_1#Вы потеряли немного очков здоровья.
msg_tuto_heal_2#Найдите способ вылечиться до того, как умрете.
msg_tuto_shops_0#Обучение: Магазины
msg_tuto_shops_1#Вы можете найти множество магазинов в подземелье.
msg_tuto_shops_2#В магазине вы сможете купить предметы или еду.
msg_tuto_boss_door_0#Обучение: Врата босса
msg_tuto_boss_door_1#Здесь вы видите закрытую золотую дверь.
msg_tuto_boss_door_2#За ней находится босс этого этажа, но вам нужен ключ, чтобы попасть внутрь.
msg_tuto_challenge_door_0#Обучение: Поле битвы
msg_tuto_challenge_door_1#Посмотрите на эту красивую дверь. За ней находится поле битвы
msg_tuto_challenge_door_2#Вы можете найти там ценную награду. Но входите с осторожностью!
msg_tuto_shots_0#Обучение: Типы выстрелов
msg_tuto_shots_1#Вы нашли новвое оружие.
msg_tuto_shots_2#Нажмите [выбор оружия] (по умолчанию: tab) чтобы переключаться между различными оружиями.
msg_tuto_spell_0#Обучение: Заклинания
msg_tuto_spell_1#Вы нашли магическое заклинание.
msg_tuto_spell_2#Нажмите на клавишу заклинания (по умолчанию: space) чтобы произнести его.
enemy_type_bat#летучей мышью
enemy_type_rat#крысой
enemy_type_rat_black#черной крысой
enemy_type_rat_helmet#крысой в шлеме
enemy_type_rat_black_helmet#черной крысой в шлеме
enemy_type_evil_flower#цветком зла
enemy_type_evil_flower_ice#эдельвейсом зла
enemy_type_snake#змеей
enemy_type_snake_blood#змеей крови
enemy_type_slime#слизнем
enemy_type_slime_red#красным слизнем
enemy_type_slime_blue#синим слизнем
enemy_type_slime_violet#фиолетовым слизнем
enemy_type_imp_blue#синим чертом
enemy_type_imp_red#красным чертом
enemy_type_pumpkin#сумасшедшей тыквой
enemy_type_witch#ведьмой
enemy_type_witch_red#красной ведьмой
enemy_type_cauldron#котлом
enemy_type_bubble#пузырем
enemy_type_boss_butcher#Свином-мясником
enemy_type_boss_slime_giant#Гигантским слизнем
enemy_type_boss_cyclops#каменным циклопом
enemy_type_boss_rat_king#Королем веркрысом
enemy_type_boss_spider_giant#Гигантским пауком
enemy_type_green_rat#зеленой крысой
enemy_type_rock_falling#падающим камнем
enemy_type_rock_missile#летящим камнем
enemy_type_spider_egg#упавшим паучьм яйцом
enemy_type_spider_little#пауком
enemy_type_spider_web#паучьей сетью
enemy_type_ghost#призраком
enemy_type_zombie#зомби
enemy_type_zombie_dark#темным зомби
enemy_type_francky#нечто
enemy_type_himself#по глупости
source_explosion#взрывом
source_poison#ядом
enemy_francky#Нечто
enemy_francky_head#Голова нечто
item_apple_green#Зеленое яблоко
item_apple_green_desc#Восстанавливает 3 HP и исцеляет яд
item_manual_slime#Руководство мастера слизней
item_manual_slime_desc#Слизни больше не охотятся на вас
item_spell_dual_quick#Книга быстрого двойного выстрела
item_spell_dual_quick_desc#Стреляет двумя снарядами (быстро)
item_spell_triple_quick#Книга быстрого тройного выстрела
item_spell_triple_quick_desc#Стреляет тремя снарядами (быстро)
item_manual_health#Руководство здоровья
item_manual_health_desc#Увеличенное лечение предметами
item_rear_shot#Наконечник для посоха
item_rear_shot_desc#Выстрел назад
item_book_random_shot#Книга случайного выстрела
item_book_random_shot_desc#Стреляет в случайном направлении
item_spell_web#Заклинание: Паучья сеть
item_spell_web_desc#Произнесите, чтобы метнуть паучьи сети
item_book_magic_i#Книга магии - Том первый
item_book_magic_i_desc#Уменьшает время восстановления заклинаний
item_book_magic_ii#Книга магии - Том второй
item_book_magic_ii_desc#Увеличивает силу заклинаний
item_sulfur#Сера
item_sulfur_desc#Позволяет взрывать трупы
spell_web#паучья сеть
tuto_reset#Сброс обучения
tuto_reset_desc#Сбрасывает обучение
msg_francky_0#Это.... Нечто...
msg_francky_1#Тьфу... Оно выглядит, слово сделано из нескольких трупов !
msg_tuto_tips_0#Обучение: Подсказки
msg_tuto_tips_1#Используйте управление временем (по умолчанию: RShift)
чтобы путешествовать быстрее.
msg_tuto_tips_2#Нажмите [Enter] чтобы немедленно закрыть окно сообщений.
menu_continue#Продолжить
menu_continue_desc#Продолжить игру
menu_save_quit#Сохранить и выйти
menu_save_quit_desc#Сохранить и вернуться в меню игры
menu_quit#Выход без сохранения
menu_quit_desc#Вернуться в главное меню без сохранения
inventory#Инвентарь
dc_certificate#Свидетельство о смерти
dc_killed_by#Убит
dc_died_level#Умер на уровне
dc_after#после
dc_minutes# минут игры
dc_killed_monsters#Убитые монстры
dc_gold#Золото
dc_challenges#Завершено битв
player_name#Имя игрока
player_name_desc#Изменить имя игрока
hi_scores#Зал славы
hi_scores_desc#Лучшие результаты
credits#Авторы
credits_desc#Список создателей
volume_sound#Громкость звука
volume_sound_desc#Используйте клавиши влево/вправо чтоы изменить громкость
volume_music#Громкость музыки
certificate_capture#Нажмите [F1] чтобы сохранить сертификат в PNG файл
certificate_saved#Сохранено в
msg_tuto_temple_0#Обучение: Храм
msg_tuto_temple_1#Храм - это место поклонения и пожертвования божеству.
msg_tuto_temple_2#Божества могут помочь вам предоставив дополнительную силу.
key_interact#взаимодействовать
interact_worship#Поклониться
interact_shop#Купить
interact_donate#Пожертвовать 10 золотых
interact_donate_fail#(не хватает золота)
worshipping#Теперь ваш покровитель
divinity_healer_0#Помоноса Праведная
divinity_healer_1#Защитница праведных и покровительница слабых.
divinity_healer_2#Она ценит пожертвования, страдания и борьбу со злом.
divinity_fighter_0#Телло Могущественный
divinity_fighter_1#Лорд сражающихся и сильных.
divinity_fighter_2#Он ценит монстров и титанов павших в битве.
divinity_ice_0#Мистер Снег
divinity_ice_1#Божество холода.
divinity_ice_2#Он ценит использование льда.
divinity_stone_0#Гайя
divinity_stone_1#Божество земли и камня.
divinity_stone_2#Он ценит использование камней.
divinity_intervention_0#Божественное вмешательство
divinity_intervention_1#вмешивается в вашу пользу.
divinity_gift_0#Божественный дар
divinity_gift_1#предоставляет вам дар.
item_floor_map#Карта подземелья
item_floor_map_desc#Открывает карту текущего этажа
item_alcohol#Абсент
item_alcohol_desc#временно увеличивает урон
achiev_rats#Родентицид
achiev_rats_desc#Убить 250 крыс
achiev_100#Скупой
achiev_100_desc#Собрать 100 золотых монет
achiev_piety_max#Верующий
achiev_piety_max_desc#Достичь максимального уровня благочестия
achiev_4_hits#Вертел
achiev_4_hits_desc#Ранить четырех монстров одним выстрелом
achiev_complete_set#Одеяние
achiev_complete_set_desc#Надеть робу, шляпу, обувь и перчатки
achiev_giant_slime#Убийца гигантского слизня
achiev_giant_slime_desc#Убить гигантского слизня
achiev_cyclops#Убийца циклопов
achiev_cyclops_desc#Убить циклопов
achiev_rat_king#Убийца веркрыса
achiev_rat_king_desc#Убить веркрысу
achiev_giant_spider#Убийца гигантского паука
achiev_giant_spider_desc#Убить гигантского паука
achiev_francky#Убийца нечто
achiev_francky_desc#Убить нечто
achiev_butcher#Убийца мясника
achiev_butcher_desc#Убить свина-мясника
achiev_witches#Инквизитор
achiev_witches_desc#Убить 50 ведьм
achiev_eggs#Испорченный омлет
achiev_eggs_desc#Уничтожить 100 яиц
achiev_vampire#Охотник на вампиров
achiev_vampire_desc#Убить вампира
achiev_challenges#Гладиатор
achiev_challenges_desc#Пройти 4 испытания
achiev_win#Жить мечтой
achiev_win_desc#Пройти демо
func_achiev_screen#Экран достижений
func_achiev_all#Описание достижений
func_double_spell#Два заклинания на выбор
enemy_type_evil_flower_fire#злым огненным цветком
enemy_type_bat_skeleton#крысой-скелетом
enemy_type_slime_large#большим слизнем
enemy_type_slime_blue_large#большим синим слизнем
enemy_type_slime_red_large#большим красным слизнем
enemy_type_slime_violet_large#большим фиолетовым слизнем
enemy_type_bogeyman#пугалом
enemy_type_cauldron_elemental#элементалем котла
menu_achievements#Достижения
menu_achievements_desc#Вывод достижений
achievement_complete#Получено достижение!
item_spell_flower#Заклинание: Призыв волшебного цветка
item_spell_flower_desc#Произнесите чтобы призвать волшебный цветок
item_rapid_shots#Книга быстрой стрельбы
item_rapid_shots_desc#Пулемет для мага
item_fairy_poison#Фея Вики
item_fairy_poison_desc#Помогает вам в подземелье
item_luck#Четырехлистный клевер
item_luck_desc#Временно увеличивает количество выпадающих денег
item_fairy_powder#Пыльца феи
item_fairy_powder_desc#Временно усиливает фей
item_ring_poison#Кольцо с изумрудом
item_ring_poison_desc#Увеличивает силу яда
item_gem_poison#Камень яда
item_gem_poison_desc#Атака ядом
item_book_prayer_i#Книга молитв - Том I
item_book_prayer_i_desc#Увеличивает благочестие
item_book_prayer_ii#Книга молитв - Том II
item_book_prayer_ii_desc#Уменьшает цену благочестия
item_pet_slime#Прислужник слизень
item_pet_slime_desc#Будет следовать за вами и помогать
item_hat_advanced#Шляпа колдуна
item_hat_advanced_desc#Сопротивление элементалям и увеличение скорости стрельбы
item_robe_advanced#Шокирующая волшебная роба
item_robe_advanced_desc#Пульсирует и увеличивает броню
item_critical#Накладная борода мага
item_critical_desc#Увеличивает шанс на критический удар (x2)
item_critical_advanced#Накладная борода архимага
item_critical_advanced_desc#Увеличивает шанс на критический удар (x3)
item_spell_fairy#Заклинание: Трансмутация фей
item_spell_fairy_desc#Произнесите чтобы трансмутировать фей
item_gloves_advanced#Перчатки стрелка
item_gloves_advanced_desc#Обезвреживает ловушки и увеличивает скорость стрельбы
item_boots_advanced#Ботинки стрелка
item_boots_advanced_desc#Увеличивают скорость
divinity_leveling_0#Уровень преданности повышен!
div_fighter_lvl#увеличивает урон
div_healer_lvl_2#предоставляет регенерацию
div_healer_lvl_3#увеличивает регенерацию
div_ice_lvl_3#увеличивает сопротивление люду
div_ice_lvl_4#усиливает атаку людом
div_ice_lvl_5#увеличивает иммунитет к заморозке
div_stone_lvl_3#увеличивает сопротивление камню
div_stone_lvl_4#усиливает атаку камнем
div_stone_lvl_5#увеличивает иммунитет пульсаций
enemy_vampire#Вампир
enemy_type_vampire#вампиром
msg_tuto_achiev_0#Обучение: Достижения
msg_tuto_achiev_1#Вы получили достижение.
msg_tuto_achiev_2#Они открывают новые предметы и бонусы.
msg_vampire_0#Вампир
msg_vampire_1#Как же я забыл взять с собой чеснок?!
msg_level7_0#Дочь архимага
msg_level7_1#"Я все ближе и ближе.
msg_level7_2#Она уже недалеко отсюда."
divinity_air_0#Острам
divinity_air_1#Божество воздуха и шторма.
divinity_air_2#Он ценит использование молний.
div_air_lvl#увеличивает скорость
div_air_lvl_4#увеличивает атаку молнией
config_joystick#Настройка джойстика
redef_joystick#Изменить управление джойстиком
joystick_configuration#Настройка джойстика
joystick_configuration_desc#Настройте джойстик, пожалуйста
joystick_configuration_insert#[нажмите кнопку]
joystick_not_found#Джойстик не подключен!
item_scroll_revelation#Свиток открытий
item_scroll_revelation_desc#Раскрывает секреты зелий
item_potion_01#Красное зелье
item_potion_02#Зеленое зелье
item_potion_03#Золотистое зелье
item_potion_04#Синее зелье
item_potion_05#Фиолетовое зелье
item_potion_06#Розовое зелье
item_potion_07#Оранжевое зелье
item_potion_08#Бирюзовое зелье
item_potion_09#Коричневое зелье
item_potion_10#Темное зелье
item_potion_desc#Эффект неизвестен
item_potion_health#Зелье лечения
item_potion_health_desc#Восстанавливает 18 HP
item_potion_poison#Ядовитое зелье
item_potion_poison_desc#Отравляет
item_potion_speed#Зелье скорости
item_potion_speed_desc#Временно увеличивает скорость
item_potion_slow#Зелье замедления
item_potion_slow_desc#Временно уменьшает скорость
item_potion_weakness#Зелье слабости
item_potion_weakness_desc#Временно уменьшает урон
item_potion_strength#Зелье силы
item_potion_strength_desc#Временно увеличивает силу
item_potion_oblivion#Зелье забвения
item_potion_oblivion_desc#Забыть карту и эффекты зелий
item_potion_confusion#Зелье запутанности
item_potion_confusion_desc#Запутывает
item_potion_cure#Зелье Табула Раса
item_potion_cure_desc#Отменяет все эффекты, наложенные на игрока
item_potion_rage#Зелье ярости
item_potion_rage_desc#Призывает олненные шары в помощь
item_bag#Сумка
item_bag_desc#Предоставляет больше места для предметов
msg_potion_id_0#Идентификация зелий
msg_potion_id_1#Вы идентифицировали:
msg_tuto_consumables_0#Обучение: Используемые предметы
msg_tuto_consumables_1#[num1-num4] чтобы использовать предмет.
msg_tuto_consumables_2#[left shift][num1-num4] чтобы выбросить его.
msg_tuto_potions_0#Обучение: Зелья
msg_tuto_potions_1#Вы нашли зелье.
msg_tuto_potions_2#Вы должны выпить или идентифицировать его, чтобы знать его эффект.
secret_found#Найдена секретная комната!
enemy_type_sausage#колбаска
enemy_type_spider_tarantula#тарантул
effect_speed#Ускорение!
effect_slow#Замедление!
effect_strength#Усиление!
effect_weakness#Слабость!
effect_confusion#Запутанность!
effect_forget#Забытие!
effect_cure#Табула Раса!
effect_rage#ЯРОСТЬ!
item_book_alchemy#Книга алхимии
item_book_alchemy_desc#Раскрывает эффект некоторых зелий
item_fairy_stone#Фея Кассандра
item_fairy_stone_desc#Помогает вам в подземелье
item_merchant#Печать гильдии торговцев
item_merchant_desc#Теперь цены в магазинах куда лучше
item_spell_time#Заклинание: Остановка времени
item_spell_time_desc#Произнесите чтобы замедлить время
item_side_shots#Книга боковых выстрелов
item_side_shots_desc#Стреляет по сторонам
item_spell_lightning#Заклинание: Молния
item_spell_lightning_desc#Произнесите, чтобы призвать молнию
item_belt_advanced#Черный пояс
item_belt_advanced_desc#Увеличивает скорость стрельбы фей
item_rear_shot_advanced#Серебряный наконечник для посоха
item_rear_shot_advanced_desc#Стреляет двумя выстрелами назад
item_amulet_retaliation#Амулет мести
item_amulet_retaliation_desc#Он заступится за меня
item_rage_amulet#Амулет ярости
item_rage_amulet_desc#Стреляет огнем
achiev_fairies#Ангелы чарли
achiev_fairies_desc#Найдите трех фей
achiev_ghost#Охотник за привидениями
achiev_ghost_desc#Убейте 50 призраков
achiev_noob#Рак
achiev_noob_desc#Умрите на первом уровне
achiev_no_damage#Крутой парень
achiev_no_damage_desc#Пройдите один уровень без потери здоровья
achiev_no_damage2#Очень крутой парень
achiev_no_damage2_desc#Пройдите два уровня без потери здоровья
achiev_no_damage3#Офигеть какой крутой парень
achiev_no_damage3_desc#Пройдите три уровня без потери здоровья
achiev_apostate#Отступник
achiev_apostate_desc#Откажитесь от 4-го уровня преданности своему божеству
achiev_fanatic#Фанатик
achiev_fanatic_desc#Пожертвуйте 100 золотых в храм
func_nothing#Ничего!
state_frozen#Заморожен
state_slow#Замедлен
state_speed#Ускорен
state_poison#Отравлен
state_weakness#Ослаблен
state_strength#Усилен
state_rage#Яростен
state_confusion#Запутан
state_time#Время остановлено
state_div_protection#Защищен (покровителем)
state_div_speed#Ускорен (покровителем)
state_div_firerate#Ускорена стрельба (покровителем)
state_div_damage#Усилен (покровителем)
dc_victory#Победный сертификат
dc_after_victory#Победа после
player#игрок
players#игрока
ui_base_damage#Базовый урон
ui_fire_rate#Скорость стрельбы
ui_second#в секунду
ui_temple_donation#Пожертвования
\ No newline at end of file
diff --git a/media/effect_zone.png b/media/effect_zone.png
new file mode 100644
index 0000000..bace6f4
Binary files /dev/null and b/media/effect_zone.png differ
diff --git a/media/sound/stone_hit.ogg b/media/sound/stone_hit.ogg
new file mode 100644
index 0000000..4a20672
Binary files /dev/null and b/media/sound/stone_hit.ogg differ
diff --git a/readme.txt b/readme.txt
index e34c512..ace4289 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,444 +1,456 @@
WITCH BLAST
============================
Author / code - art - music: Seby
Email: sebygames@gmail.com
2014 - 2015
============================
Introduction
------------
Witch Blast is a free roguelite dungeon crawl shooter heavily inspired from Binding Of Isaac. The player plays as a novice magician in a dungeon who is trying to get as far as he can, using various items he can find to defeat the inhabitants of the dungeon.
Web: https://github.com/Cirrus-Minor/witchblast
Install
-------
If there is no binary for your version, you have to compile the application. You will need the library SFML, minimal version 2.2.
A CMake file is available.
Commands
--------
Standard commands (can be changed in the game):
WASD (US, DE) or QSDZ (FR) to move in 8 directions
---
Arrows to shoot in 4 directions,
or
[Right CTRL] (can be hold) to shoot in the facing direction.
or
Mouse left-click
---
[Tab] to change shoot type (if you've found any).
or
Mouse Wheel
---
[Space] to cast a spell (if you've found any).
Saving
--------
Game saves automatically when leaving the game in a cleared area.
Leaving the game while fighting destroys the current game.
Features
--------
- randomly generated dungeons,
- powerful items,
- different shot types,
- monsters,
- chest and keys,
- merchant,
- spells with cooldown,
- temples and divinities,
- achievements and bonus/items unlock,
- candy eye effects.
Credits
-------
Seby: code and music
Pierre "dejam0rt" Baron: 2D art
Le Guitariste Du Nord: Music - https://soundcloud.com/leguitaristedunord
R0d: particles system and multilingual support coding
Cong: Multiplayer mode and other contributions (https://github.com/cxong)
Licenses
--------
Code: GPLv3
Art (2D and music): CC BY-NC-SA
Thanks
--------
Thanks to
Vetea (for the v0.4.0 2D redesign)
Sideous and Portoss01 (they provide me 2D art in earlier versions)
Billx (who suggested me the title "Witch Blast" + English corrections)
Mika Attila (for his contribution during the development)
Geheim (for the German translation)
Achpile (for the Russian version and his help in the code)
AFS (for the Spanish translation)
Changes
-------
+Next release
+- ADD 3 new divinities
+- ADD New effects for lvl 3 attacks
+- MOD New divinity presents (rings)
+- MOD Fireball spell damage increase
+- MOD Explosive barrel don't explode the first 2 seconds in a room
+- MOD Slime from a cauldron or a giant slime can't directly hurt the player while jumping the first time
+- MOD Divinity trigger more complex now
+- MOD New data save system (compatibility with future versions)
+- FIX Secret room misplacement
+- FIX Stone divinity appreciates ice attacks
+
v0.7.5
- ADD 2 players mode
- ADD Score display in game
- ADD New game and pause UI
- ADD Player scores are shining in score screens
- ADD German, Spanish and Russian translations
- ADD Mouse support for using/dropping consumables and switching bolt type
- ADD Config option to turn off the pause while losing the focus
- ADD Config option to turn off sprite batching of particles
- ADD Config option to decrease lifetime of particles (for old computers)
- ADD Potion description in the pause screen
- MOD Sausages can now be killed in one shot (standard damage)
- MOD Lightning spell deals progressive damage (5 dam in level 3, 6 in level 4, etc...)
- MOD Increase of the divine protection duration
- MOD Effect cooldown are now in the upper part of the interface
- MOD Cooldown to pick up a potion we've dropped is now 5 seconds
- MOD Explosive barrel is now red with an explosion symbol
- MOD Some barrels are damaged at generation
- MOD "Bomberman"-type rooms have always barrels at level 4 and above now
- MOD Green blood and slime corpses have been desaturated for a better game visibility
- MOD Level up divinity effect has been changed (was too close of the divinity intervention effect)
- FIX Inputs are processed while the game window has no focus
- FIX Doors don't open when there is still spider webs in room
- FIX Spider webs slow down some spiders
- FIX Grid on walls
- FIX The player effects (poison, slow, damage up, etc...) are not saved
- FIX Bag appears only when the player has boots
- FIX No door to secret room on loaded game
- FIX Item order in the inventory
- FIX It's possible to let the player name blank
- FIX It's possible to drop potions we don't have
- FIX Closing the game in the pause screen don't save the game
v0.7
- ADD Destructible tiles
- ADD Potions
- ADD Secret rooms
- ADD New items
- ADD New familiar
- ADD New magic spells
- ADD New achievements
- ADD New music
- ADD Player effects display
- ADD Blinking effect when player is invincible
- ADD Monsters
- MOD Consumables in shops
- MOD Evil flower damage
- MOD Butcher can drop sausages now
- MOD Game becomes harder for good players
- MOD Some monsters (rats, bats) slow down when changing direction
- MOD Level 8 is the last level now
- MOD Air divinity fury has now a lifetime (was infinite)
- MOD Intro is displayed just once now
- MOD Display the best player instead of the best scores
- MOD We don't have to clear the room to get the achievement now
- MOD In "advanced" mode, the "fairy cursor" changes
- MOD Bolt particles are now batched
- FIX Latency when communicating with the score server
- FIX Interaction key has to be redefined each game
- FIX Item description text too large
v0.6
- ADD Online scores
- ADD Player movement improvements
- ADD More details in dungeons
- ADD New doors
- ADD New walls
- ADD New art for the "bubble"
- ADD Flower animation
- ADD Joystick support
- MOD Stone divinity repulse fury causes damage now (8HP)
- MOD Fireball (from spell) minimum damage increases (from 12 to 24 HP)
- FIX Explosive slime can be stuck in the wall when casting "invoke slime" spell
- FIX Horizontal lines when maximizing the screen
- FIX Spell lost when taking two spells at the same time
- FIX Missing art for advanced gloves and boots
- FIX Some random sprites are misplaced
- FIX Graphical problems on The Thing
- FIX Pumpkin sound is playing twice
- FIX Giant slime impact sound was missing
v0.5.3
- ADD Glowing green staff aura when poison shot equipped
- ADD Air divinity aura
- ADD New room patterns
- ADD Better looking dungeon: Wall shadow, decorations, better tiles
- ADD German and Russian translations
- MOD An item can be only dropped once in a level
- MOD Giant Spider difficulty slightly increases
- MOD Ghost are shortly visible at fight start
- MOD Gloves and advanced gloves switched
- MOD Snakes from trap can drop golds or apples
- MOD Elemental cauldron invoke rate decreases and is limited (no spawn when more than 8 monsters on screen)
- MOD Fairy powder does not teleport the player as often as it used too
- FIX Wrong air divinity conversion message
- FIX Overlapping items descriptions
- FIX No ghost dying animation
- FIX Key zone tiles were wrong
v0.5
- ADD A new level (7)
- ADD New boss (for level 7)
- ADD Achievements and unlocks (16)
- ADD Items
- ADD Monsters and variants
- ADD Shot type (poison)
- ADD Spells (2)
- ADD Familiars (a fairy and another one)
- ADD Chest traps
- ADD Challenge levels 3 and 4
- ADD Tiles and room patterns
- ADD Divinity level messages
- ADD Tree Divinity has a fury against undead
- MOD No player collision while invincible
- MOD Sulfur causes the monster explode directly, and safe for the player
- MOD Giant Spider easier, and less green blood on screen
- MOD Divine interventions have a piety cost
- MOD Hiccup makes the player fire poison
- MOD Imps do not teleport with 1 HP damage
- MOD Corpse can fall in holes
- MOD Completed challenge chest can contain items (but it's rare)
- FIX Natasha and Scarlett wrong display in Hall of Fame
- FIX V-Sync does not work on some GPU
v0.4.6b
- ADD Russian translation
- FIX No more texture bigger than 2048x2048 for better compatibility
- FIX Wrong filename
v0.4.6
- ADD German and Spanish translations
- ADD Improved and new sound (each shot type has a different sound effect)
- ADD Full minimap on the pause menu
- ADD Full screen mode (experimental)
- MOD Short latency before teleport when an imp has been hurt
- MOD Exploding enemy shows "burned" sprite
- MOD Improved snake AI
- FIX No player stats update when levelling up piety level
- FIX Shadows missing (items and eggs) or misplaced (Cyclops)
- FIX Label errors (missing French translation, or wrong key error for English version)
- FIX False bolt sprite for The Thing
- FIX Scarlet and Natasha sprites are switched
- FIX Unexpected divine intervention messages
- FIX Teleport on key is possible
v0.4.5
- ADD Temple (worship + donation)
- ADD Divinities (4)
- ADD Divinity powers and interventions
- ADD Interaction key
- ADD "level" objects
- ADD New art and GUI
- MOD Shop interaction
- MOD New Key room
v0.4.1
- ADD Scores and hi-score screen
- ADD Blood is spraying now
- ADD Sound and music volume in config menu
- ADD Options to enable/disable zoom, vssync, blood spraying, in config file
- ADD Auto-save at the beginning of a fight (game is not wasted in case of crash or freeze)
- MOD Level 5 and level 6 are easier now
- MOD Sorted inventory
- MOD Optimizations (use of vertex arrays)
- MOD Don't distort game screen when resizing the window
- MOD Corpses don't stay on walls
- FIX Credit screen crash while pressing arrow
v0.4.0
- ADD New level
- ADD New monsters
- ADD New items
- ADD New spell
- ADD New art for sprites and tiles
- ADD New room surfaces
- ADD New intro and menu
- ADD Levels have different tilesets now
- ADD In-game menu (pause)
- ADD Inventory display in the in-game menu
- ADD Chest for completed challenges
- ADD Tutorial state save and reset
- ADD Cauldron now explodes while dying
- ADD Sound for imp's teleportation
- ADD "Jump" animation for items you can get
- ADD Zoom effect when starting / dying
- ADD In-fight save game
- ADD Mouse right click support (spell)
- ADD Show selected key in key configuration
- ADD Credits screen
- MOD Game is faster now
- MOD Standard Rat IA changes (rats now move in 4 directions)
- MOD Frozen dividing bubbles generate frozen "children" bubbles
- MOD Fireball (spell) now goes through monsters (if enough HP)
- MOD Lightning bolt now goes through monsters (if enough HP)
- MOD Cyclops' damage decreases
- MOD Giant Spider is more difficult to kill now
- MOD Shop's items too expensive displayed in red
- MOD One cauldron max in witches rooms in level 5
- MOD New death screen
- MOD Exploding fire shot replaced with sulfur item
- MOD Exploding corpse with timer now
- MOD Snake AI changes (no more "group attack)
- MOD Witches and Flowers use ray casting to fire or not
- MOD Cauldron + Witches only on rooms with walls
- MOD Bats and little slime HP-
- MOD "Elemental" monsters resistance or weakness increase
- MOD Double and triple quick shots don't do so much damage when cumulated with damage items
- FIX Cyclops firing in the wall when too close to the north wall
- FIX Items can fly a long time on the walls
- FIX Clean message queue while starting a new game
v0.3.7
- ADD New monsters (snakes)
- ADD New items (spell orbs)
- ADD Poison
- ADD Cold bolt (generated with an unloaded ice shot)
- ADD Spells (5 different ones)
- ADD Translations: Spanish and Russian
- ADD New art for bats
- ADD Texts "widgets" for story and tutorial
- MOD Vibration Gloves are now Displacement Glove (player's velocity impacts bolt velocity)
- MOD Items presentation windows colour has changed
- MOD Repulsion improved
- MOD Giant Slime HP decreased
- MOD Cyclops melee and missile damage decreased, now repulse
- MOD A merchant on each floor > 1 now
- MOD Force Challenge room for each floor > 2 if no spell is equipped
- FIX Texts are now in UTF-8
- FIX Monsters out of the game zone
- FIX Persistent particles on some machines/systems
v0.3.3
- ADD German version
- FIX Crash when getting the quartz ring
v0.3.2
- ADD Particles for player's missiles
- ADD Death Screen
- ADD Force shop generation if there is no one in the previous floor
- ADD Display gold when getting coins
- ADD New starting screen with language selection (English or French) and standard keyboard configuration
- MOD Invert Cyclop and Rat King bosses
- MOD Dual bolt angle is smaller
- MOD Damage's display size increases with damage
- MOD Rat with helmet are slower
- MOD Green rats' don't collide while spawning
- MOD Fairies don't stay stuck in wall/door
- MOD Player's bounding box is now smaller
- MOD Rat and bat's dying sound's volume is lower now
- MOD Better Bubble physics
- MOD Target Fairy (Scarlett) firerate decreased
- FIX Wrong HP display (bar) when a boss is dying
- FIX Items are not dropped out of screen anymore
v0.3.0
- ADD New level with a new boss
- ADD New items
- ADD New familiars (fire and target fairies)
- ADD New monsters variants (rats with helmet, violet slime)
- ADD New monsters (bubble, witches, cauldron)
- ADD New shot type (fire)
- ADD Challenge Room
- ADD Explosions
- ADD Chest's trap
- ADD New tiles : holes
- ADD New decorative tiles
- ADD Display of health items on the minimap
- ADD Display of the damage in the game area
- ADD New graphics for Rat King
- MOD Health items drop's rate decreases
- MOD Rat King berserk's mode is easier now
- MOD Cyclops' falling stones bounce (one time)
- MOD Cyclops has protection before stones are falling
- MOD Butcher drops a silver coin now
- MOD Evil Flower missile's damage decreased
- FIX Sprite center and Z-order
- FIX Giant Slime bug when falling on the wall
- FIX Freeze when opening a chest while having all the items (but not the fairies)
- FIX Player does not bounce while being repulsed
v0.2.5
- ADD New mini-boss
- ADD New first level - easier
- ADD Time control (2x faster)
- ADD Display of HP/HPmax on the life bar
- ADD New health items: apple, bread, cheese (no more potions)
- ADD Monsters can drop health item
- MOD Shot distance is longer
- MOD Acquiring stance can be broken after the half of the time
- MOD Changes in item' level
- MOD Blood Snake damage's bonus decreases
- MOD Merchant sells cheese an bread now
- FIX Lost item when leaving the game while acquiring the item
- FIX Monsters can leave the game zone while lagging or windows moving
- FIX Player can walk through south wall at the door
- FIX Player's death display artefacts
v0.2.0
- ADD New familiar (Ice Fairy)
- ADD New items (4 rings)
- ADD New monster and variants
- ADD New level
- ADD New boss
- ADD Lightning's shot effect and sound
- ADD Special shots levels
- ADD Menu screen
- ADD Keys configuration
- ADD Death animation and sound for bats and rats
- MOD Bolt's bounding box is bigger when testing collision with monsters
- MOD Player's invincibility delay after being shot increases
- MOD Player's diagonal velocity decreases
- MOD Bosses are now in the middle of the room, 2 seconds idle
- MOD Giant Slime's latency decreases
- MOD Resistance and status immunity new system (boss can be frozen now, although it's difficult)
- MOD Player's step sound removed
- MOD New game with [LShift]+[X] now (used to be [X] alone)
- MOD Tutorial's text (first room) removed
- FIX Switching windows while player is dead
- FIX Player can fire while getting an item
- FIX Player does not drop fairies anymore when dying
- FIX Giant Slime life bar corrected
- FIX King Rat protection while berserk
v0.1.0
- ADD New enemies (slime and black rat)
- ADD New items
- ADD Item's rarity
- ADD Show player's impact when he's hurt
- ADD New music when the player dies
- ADD New music for boss' fight
- ADD Mouse support - Player can fire with left click, switch shot types with the wheel
- ADD Pause
- ADD Save / restore game
- ADD Slowing (creature state)
- ADD HUD for shot types
- ADD 4 shot types
- ADD Recoil
- ADD New room templates
- ADD Exit stairs
- ADD Collisions between monsters
- ADD Screen's shaking effect
- ADD Configuration file
- ADD New tile's graphics
- MOD Damages inflicted by the player have been modified
- MOD The player's bolt size depend of the damages
- MOD Only useful data displayed on the minimap
- MOD Reduced bounding box for the player's bolt
- MOD Vertical Synchronisation
- MOD 2 health potions in shops now
- FIX Fade in / fade out text bugs
- FIX Merchant z-ordering bug
- FIX It was possible to begin at the exit
- FIX Various bad references crashes
- FIX Double shot used to double drop
\ No newline at end of file
diff --git a/src/BaseCreatureEntity.cpp b/src/BaseCreatureEntity.cpp
index dfc58e4..9e7e64d 100644
--- a/src/BaseCreatureEntity.cpp
+++ b/src/BaseCreatureEntity.cpp
@@ -1,850 +1,852 @@
#include "BaseCreatureEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "ExplosionEntity.h"
#include "TextMapper.h"
#include <iostream>
#include <sstream>
BaseCreatureEntity::BaseCreatureEntity(sf::Texture* image, float x = 0.0f, float y = 0.0f, int spriteWidth = -1, int spriteHeight = -1)
: CollidingSpriteEntity (image, x, y, spriteWidth, spriteHeight )
{
hurting = false;
displayDamage = true;
hurtingType = ShotTypeStandard;
shadowFrame = -1;
setMap(game().getCurrentMap(), TILE_WIDTH, TILE_HEIGHT, 0, 0);
hpDisplay = 0;
armor = 0.0f;
movingStyle = movWalking;
for (int i = 0; i < NB_SPECIAL_STATES; i++)
{
specialState[i].type = (enumSpecialState)i;
specialState[i].active = false;
specialState[i].timer = 0.0f;
specialState[i].param1 = 0.0f;
specialState[i].param2 = 0.0f;
specialState[i].param3 = 0.0f;
specialState[i].waitUnclear = false;
}
for (int i = 0; i < NB_RESISTANCES; i++)
{
resistance[i] = ResistanceStandard;
}
- recoil.active = false;
+ repulsion.active = false;
facingDirection = 2;
doesAccelerate = false;
}
int BaseCreatureEntity::getHp()
{
return hp;
}
int BaseCreatureEntity::getHpMax()
{
return hpMax;
}
void BaseCreatureEntity::setHp(int hp)
{
this->hp = hp;
}
void BaseCreatureEntity::setHpMax(int hpMax)
{
this->hpMax = hpMax;
}
int BaseCreatureEntity::getHpDisplay()
{
return hpDisplay;
}
float BaseCreatureEntity::getCreatureSpeed()
{
return creatureSpeed;
}
IntCoord BaseCreatureEntity::getCurrentTile()
{
int xMap = (x) / TILE_WIDTH;
int yMap = (y) / TILE_HEIGHT;
return (IntCoord(xMap, yMap));
}
BaseCreatureEntity::enumMovingStyle BaseCreatureEntity::getMovingStyle()
{
return movingStyle;
}
bool BaseCreatureEntity::isSpecialStateActive(enumSpecialState state)
{
return specialState[state].active;
}
specialStateStuct BaseCreatureEntity::getSpecialState(enumSpecialState state)
{
return specialState[state];
}
void BaseCreatureEntity::setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2,bool waitUnclear)
{
specialState[state].active = active;
specialState[state].timer = timer;
specialState[state].param1 = param1;
specialState[state].param2 = param2;
specialState[state].waitUnclear = waitUnclear;
}
float BaseCreatureEntity::animateStates(float delay)
{
for (int i = 0; i < NB_SPECIAL_STATES; i++)
{
if (specialState[i].active)
{
specialState[i].timer -= delay;
if (specialState[i].timer <= 0.0f)
{
setSpecialState((enumSpecialState)i, false, 0.0f, 0.0f, 0.0f);
game().getPlayer()->computePlayer();
if (i == SpecialStateTime) game().resumeMusic();
}
}
else if (specialState[i].waitUnclear && !game().getCurrentMap()->isCleared())
{
specialState[i].waitUnclear = false;
specialState[i].active = true;
game().getPlayer()->computePlayer();
}
}
// ice
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
// poison
if (specialState[SpecialStatePoison].active)
{
specialState[SpecialStatePoison].param3 -= delay;
if (specialState[SpecialStatePoison].param3 <= 0.0f)
{
specialState[SpecialStatePoison].param3 += specialState[SpecialStatePoison].param2;
// TODO
hurt(getHurtParams(specialState[SpecialStatePoison].param1, ShotTypeDeterministic, 0, false, SourceTypePoison, NB_ENEMY, false));
}
}
return delay;
}
void BaseCreatureEntity::animateColors(float delay)
{
// no color
sprite.setColor(sf::Color(255, 255, 255, 255 ));
if (hurting && hp > 0)
{
hurtingDelay -= delay;
if (hurtingDelay > 0.0f)
{
int fadeColor = (sf::Uint8)((HURTING_DELAY - hurtingDelay) * 255);
if (hurtingDelay > HURTING_DELAY) fadeColor = 0;
if (hurtingType == ShotTypeIce || hurtingType == ShotTypeCold)
sprite.setColor(sf::Color(fadeColor, fadeColor, 255, 255 )); // blue
else if (hurtingType == ShotTypePoison)
sprite.setColor(sf::Color(fadeColor, 255, fadeColor, 255 )); // green
else
sprite.setColor(sf::Color(255, fadeColor, fadeColor, 255 )); // red
}
else
{
hurting = false;
sprite.setColor(sf::Color(255, 255, 255, 255 ));
}
}
if (specialState[SpecialStateIce].active) sprite.setColor(sf::Color(100, 100, 255, 255 ));
else if (specialState[SpecialStatePoison].active)
{
int fade = 180 + 70 * cos(age * 5);
sprite.setColor(sf::Color(fade, 255, fade, 255 ));
}
}
void BaseCreatureEntity::animateRecoil(float delay)
{
- // recoil
- if (recoil.active)
+ // repulsion
+ if (repulsion.active)
{
- recoil.velocity.x *= 0.97f;
- recoil.velocity.y *= 0.97f;
+ repulsion.velocity.x *= 0.97f;
+ repulsion.velocity.y *= 0.97f;
- recoil.timer -= delay;
- if (recoil.timer <= 0.0f)
+ repulsion.timer -= delay;
+ if (repulsion.timer <= 0.0f)
{
- recoil.active = false;
+ repulsion.active = false;
computeFacingDirection();
// TODO ?
}
}
}
void BaseCreatureEntity::animatePhysics(float delay)
{
if (doesAccelerate)
{
velocity.x += acceleration.x;
velocity.y += acceleration.y;
if (velocity.x * velocity.x + velocity.y * velocity.y > creatureSpeed * creatureSpeed)
{
float l = sqrtf(velocity.x * velocity.x + velocity.y * velocity.y);
velocity.x = (velocity.x / l) * creatureSpeed;
velocity.y = (velocity.y / l) * creatureSpeed;
doesAccelerate = false;
}
}
velocity.x *= viscosity;
velocity.y *= viscosity;
float velx = velocity.x;
float vely = velocity.y;
if (specialState[SpecialStateSlow].active)
{
velx *= specialState[SpecialStateSlow].param1;
vely *= specialState[SpecialStateSlow].param1;
}
if (specialState[SpecialStateSpeed].active)
{
velx *= specialState[SpecialStateSpeed].param1;
vely *= specialState[SpecialStateSpeed].param1;
}
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun)
+ if (repulsion.stun)
{
velx = 0.0f;
vely = 0.0f;
}
- velx += recoil.velocity.x;
- vely += recoil.velocity.y;
+ velx += repulsion.velocity.x;
+ vely += repulsion.velocity.y;
}
spin *= viscosity;
angle += spin * delay;
if (isCollidingWithMap())
{
stuck();
}
else
{
if ((int)velx > 0)
{
x += velx * delay;
if (collideWithMap(DIRECTION_LEFT))
{
x = (float)((int)x);
while (collideWithMap(DIRECTION_LEFT))
x--;
collideMapRight();
}
else if (x > map->getWidth() * tileWidth + offsetX)
{
exitMap(DIRECTION_RIGHT);
}
}
else if ((int)velx < 0)
{
x += velx * delay;
if (collideWithMap(DIRECTION_RIGHT))
{
x = (float)((int)x);
while (collideWithMap(DIRECTION_RIGHT))
x++;
collideMapLeft();
}
else if (x < offsetX)
{
exitMap(DIRECTION_LEFT);
}
}
}
vely += weight * delay;
if ( vely > maxY) vely = maxY;
if ((int)vely > 0)
{
y += vely * delay;
if (collideWithMap(DIRECTION_BOTTOM))
{
y = (float)((int)y);
while (collideWithMap(DIRECTION_BOTTOM))
y--;
collideMapBottom();
}
}
else if ((int)vely < 0)
{
y += vely * delay;
if (collideWithMap(DIRECTION_TOP))
{
y = (float)((int)y);
while (collideWithMap(DIRECTION_TOP))
y++;
collideMapTop();
}
}
if (lifetime > 0)
{
if (age >= lifetime) dyingFromAge();
}
age += delay;
}
void BaseCreatureEntity::dyingFromAge()
{
dying();
}
void BaseCreatureEntity::animate(float delay)
{
if (hpDisplay > hp) hpDisplay--;
else if (hpDisplay < hp) hpDisplay++;
delay = animateStates(delay);
animateColors(delay);
animateRecoil(delay);
animatePhysics(delay);
z = y + height/2;
}
void BaseCreatureEntity::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
int nx = shadowFrame;
int ny = 0;
if (imagesProLine > 0 && shadowFrame >= imagesProLine)
{
nx = shadowFrame % imagesProLine;
ny = shadowFrame / imagesProLine;
}
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
}
CollidingSpriteEntity::render(app);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void BaseCreatureEntity::calculateBB()
{
}
bool BaseCreatureEntity::collideWithMap(int direction)
{
calculateBB();
int xTile0 = (boundingBox.left - offsetX) / tileWidth;
int xTilef = (boundingBox.left + boundingBox.width - offsetX) / tileWidth;
int yTile0 = (boundingBox.top - offsetY) / tileHeight;
int yTilef = (boundingBox.top + boundingBox.height - offsetY) / tileHeight;
if (boundingBox.top < 0) yTile0 = -1;
switch (direction)
{
case DIRECTION_RIGHT:
if (xTilef > xTile0) xTilef = xTile0;
break;
case DIRECTION_LEFT:
if (xTilef > xTile0) xTile0 = xTilef;
break;
case DIRECTION_TOP:
if (yTilef > yTile0) yTilef = yTile0;
break;
case DIRECTION_BOTTOM:
if (yTilef > yTile0) yTile0 = yTilef;
break;
}
for (int xTile = xTile0; xTile <= xTilef; xTile++)
for (int yTile = yTile0; yTile <= yTilef; yTile++)
{
if (movingStyle == movWalking)
{
if (isAttacking())
{
if ( dynamic_cast<DungeonMap*>(map)->isWalkable(xTile, yTile) == false
&& dynamic_cast<DungeonMap*>(map)->getLogicalTile(xTile, yTile) != LogicalDestroyable)
return true;
}
else
{
if ( dynamic_cast<DungeonMap*>(map)->isWalkable(xTile, yTile) == false )
return true;
}
}
else if (movingStyle == movFlying)
{
if ( dynamic_cast<DungeonMap*>(map)->isFlyable(xTile, yTile) == false ) return true;
}
}
return false;
}
bool BaseCreatureEntity::determineSatusChance(enumStateResistance resistance, int level)
{
bool hit = true;
switch (resistance)
{
case ResistanceVeryLow:
case ResistanceLow:
case ResistanceStandard: hit = true; break;
case ResistanceHigh: hit = rand() % 8 <= level * 2; break;
case ResistanceVeryHigh: hit = rand() % 10 <= level * 2; break;
case ResistanceImmune: hit = false; break;
}
return hit;
}
int BaseCreatureEntity::determineDamageBonus(enumStateResistance resistance, int level)
{
int bonus = 0;
switch (resistance)
{
case ResistanceVeryLow: bonus = 40 + 10 * level; break;
case ResistanceLow: bonus = 20 + 5 * level; break;
case ResistanceStandard: bonus = 0; break;
case ResistanceHigh: bonus = -25 + 5 * level; break;
case ResistanceVeryHigh: bonus = -50 + 5 * level; break;
case ResistanceImmune: bonus = -100 + 5 * level; break;
}
return bonus;
}
bool BaseCreatureEntity::textTooClose(TextEntity* textEntity, float xDistMin, float yDistMin)
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() == ENTITY_FLYING_TEXT)
{
if (e != textEntity)
{
if (textEntity->getX() - e->getX() < xDistMin && textEntity->getX() - e->getX() > - xDistMin
&& textEntity->getY() - e->getY() < yDistMin && textEntity->getY() - e->getY() > - yDistMin)
return true;
}
}
}
return false;
}
void BaseCreatureEntity::makeExplode()
{
new ExplosionEntity(x, y, ExplosionTypeStandard, 16, EnemyTypeNone, false);
game().addCorpse(x, y, FRAME_CORPSE_SLIME_VIOLET);
SoundManager::getInstance().playSound(SOUND_BOOM_00);
}
int BaseCreatureEntity::hurt(StructHurt hurtParam)
{
int oldHp = hp;
int absorbedHp = 0;
bool poisoned = false;
hurtingType = hurtParam.hurtingType;
if (armor > 0.01f && hurtingType != ShotTypeDeterministic)
{
absorbedHp = hurtParam.damage * armor;
if (absorbedHp == 0) absorbedHp = 1;
hurtParam.damage -= absorbedHp;
}
if (hurtingType == ShotTypeIce
&& determineSatusChance(resistance[ResistanceFrozen], hurtParam.level))
{
// frozen ?
specialState[SpecialStateIce].active = true;
float frozenDelayMult = 1.0f;
float frozenMultAdd = 0.0f;
if (resistance[ResistanceFrozen] == ResistanceHigh)
{
if (hurtParam.level == 0)
{
frozenDelayMult = 0.8f;
frozenMultAdd = 0.2f;
}
else
{
frozenDelayMult = 0.85f;
frozenMultAdd = 0.15f;
}
}
else if (resistance[ResistanceFrozen] == ResistanceVeryHigh)
{
if (hurtParam.level < 2)
{
frozenDelayMult = 0.7f;
frozenMultAdd = 0.25f;
}
else
{
frozenDelayMult = 0.8f;
frozenMultAdd = 0.2f;
}
}
specialState[SpecialStateIce].timer = STATUS_FROZEN_DELAY[hurtParam.level] * frozenDelayMult;
specialState[SpecialStateIce].param1 = STATUS_FROZEN_MULT[hurtParam.level] + frozenMultAdd;
}
else if (hurtingType == ShotTypePoison && determineSatusChance(resistance[ResistancePoison], hurtParam.level))
{
specialState[SpecialStatePoison].active = true;
specialState[SpecialStatePoison].timer = POISON_TIMER[hurtParam.level];
specialState[SpecialStatePoison].param1 = POISON_DAMAGE[hurtParam.level];
specialState[SpecialStatePoison].param2 = POISON_DELAY[hurtParam.level];
specialState[SpecialStatePoison].param3 = POISON_DELAY[hurtParam.level];
poisoned = true;
}
// damage bonus
if (hurtingType == ShotTypeIce || hurtingType == ShotTypeCold)
hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistanceIce], hurtParam.level)) / 100;
else if (hurtingType == ShotTypeFire)
hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistanceFire], hurtParam.level)) / 100;
else if (hurtingType == ShotTypeLightning)
hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistanceLightning], hurtParam.level)) / 100;
else if (hurtingType == ShotTypeStone)
hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistanceStone], hurtParam.level)) / 100;
else if (hurtingType == ShotTypeIllusion)
hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistanceIllusion], hurtParam.level)) / 100;
//else if (hurtingType == ShotTypePoison)
// hurtParam.damage += (hurtParam.damage * determineDamageBonus(resistance[ResistancePoison], hurtParam.level)) / 100;
if (hurtParam.damage > 0)
{
hurting = true;
hurtingDelay = HURTING_DELAY;
this->hurtingType = hurtingType;
hp -= hurtParam.damage;
if (hp <= 0)
{
hp = 0;
prepareDying();
// exploding ?
if (game().getPlayer()->isEquiped(EQUIP_SULFUR) && canExplode)
{
int luck = hurtingType == ShotTypeFire ? 33 : 25;
if (rand() % 100 < luck) makeExplode();
}
}
// display damage
if (displayDamage && oldHp > 0)
{
int displayedDamage = hurtParam.damage;
if (hurtParam.goThrough && hp == 0)
{
hurtParam.damage = oldHp + absorbedHp;
displayedDamage = oldHp;
}
std::ostringstream oss;
oss << "-" << displayedDamage;
int textSize;
if (displayedDamage < 8) textSize = 17;
else textSize = 17 + (displayedDamage - 3) / 5;
TextEntity* text = new TextEntity(oss.str(), textSize, x, y - 20.0f);
text->setColor(TextEntity::COLOR_FADING_RED);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
while (textTooClose(text, 15, 15)) text->setY(text->getY() - 5);
if (hurtParam.critical)
{
std::ostringstream crss;
crss << tools::getLabel("critical");
if (game().getPlayer()->isEquiped(EQUIP_CRITICAL_ADVANCED))
crss << " X3";
else
crss << " X2";
displayFlyingText(x, text->getY() - 16.0f, 16, crss.str(), TextEntity::COLOR_FADING_RED);
}
if (poisoned)
{
displayFlyingText(x, text->getY() - 16.0f, 16, tools::getLabel("poison"), TextEntity::COLOR_FADING_RED);
}
if (hurtParam.critical) SoundManager::getInstance().playSound(SOUND_CRITICAL);
}
}
return hurtParam.damage;
}
void BaseCreatureEntity::displayFlyingText(float xText, float yText, int sizeText, std::string text, TextEntity::colorTypeEnum color)
{
TextEntity* textEntity = new TextEntity(text, sizeText, xText, yText);
textEntity->setColor(color);
textEntity->setAge(-0.6f);
textEntity->setLifetime(0.3f);
textEntity->setWeight(-60.0f);
textEntity->setZ(2000);
textEntity->setAlignment(ALIGN_CENTER);
textEntity->setType(ENTITY_FLYING_TEXT);
}
void BaseCreatureEntity::prepareDying()
{
dying();
}
void BaseCreatureEntity::dying()
{
isDying = true;
}
void BaseCreatureEntity::computeFacingDirection()
{
if (abs((int)velocity.x) > 0 || abs((int)velocity.y) > 0)
{
if (abs((int)velocity.x) > abs((int)velocity.y))
{
if (velocity.x > 0.0f) facingDirection = 6;
else facingDirection = 4;
}
else
{
if (velocity.y > 0.0f) facingDirection = 2;
else facingDirection = 8;
}
}
}
-void BaseCreatureEntity::giveRecoil(bool stun, Vector2D velocity, float timer)
+void BaseCreatureEntity::giveRepulsion(bool stun, Vector2D velocity, float timer)
{
- if (resistance[ResistanceRecoil] == ResistanceHigh)
+ if (resistance[ResistanceRepulsion] == ResistanceHigh)
{
velocity.x *= 0.75f;
velocity.y *= 0.75f;
timer *= 0.75f;
}
- else if (resistance[ResistanceRecoil] == ResistanceVeryHigh)
+ else if (resistance[ResistanceRepulsion] == ResistanceVeryHigh)
{
velocity.x *= 0.5f;
velocity.y *= 0.5f;
timer *= 0.5f;
}
- if (!(recoil.active && recoil.stun))
+ if (!(repulsion.active && repulsion.stun))
{
- recoil.active = true;
- recoil.stun = stun;
+ repulsion.active = true;
+ repulsion.stun = stun;
- if (this->velocity.x > 1.0f && velocity.x > 1.0f) recoil.velocity.x = velocity.x + this->velocity.x;
- else if (this->velocity.x < -1.0f && velocity.x < -1.0f) recoil.velocity.x = velocity.x + this->velocity.x;
- else recoil.velocity.x = velocity.x;
+ if (this->velocity.x > 1.0f && velocity.x > 1.0f) repulsion.velocity.x = velocity.x + this->velocity.x;
+ else if (this->velocity.x < -1.0f && velocity.x < -1.0f) repulsion.velocity.x = velocity.x + this->velocity.x;
+ else repulsion.velocity.x = velocity.x;
- if (this->velocity.y > 1.0f && velocity.y > 1.0f) recoil.velocity.y = velocity.y + this->velocity.y;
- else if (this->velocity.y < -1.0f && velocity.y < -1.0f) recoil.velocity.y = velocity.y + this->velocity.y;
- else recoil.velocity.y = velocity.y;
+ if (this->velocity.y > 1.0f && velocity.y > 1.0f) repulsion.velocity.y = velocity.y + this->velocity.y;
+ else if (this->velocity.y < -1.0f && velocity.y < -1.0f) repulsion.velocity.y = velocity.y + this->velocity.y;
+ else repulsion.velocity.y = velocity.y;
- recoil.timer = timer;
+ repulsion.timer = timer;
}
}
-void BaseCreatureEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void BaseCreatureEntity::inflictsRepulsionTo(BaseCreatureEntity* targetEntity)
{
}
bool BaseCreatureEntity::canCollide()
{
return true;
}
void BaseCreatureEntity::generateStar(sf::Color starColor)
{
game().generateStar(starColor, x, y);
}
bool BaseCreatureEntity::intersectsSegments(Vector2D a1, Vector2D a2, Vector2D b1, Vector2D b2)
{
Vector2D b(a2.x - a1.x, a2.y - a1.y);
Vector2D d(b2.x - b1.x, b2.y - b1.y);
float bDotDPerp = b.x * d.y - b.y * d.x;
// if b dot d == 0, it means the lines are parallel so have infinite intersection points
if (bDotDPerp == 0) return false;
Vector2D c(b1.x - a1.x, b1.y - a1.y);
float t = (c.x * d.y - c.y * d.x) / bDotDPerp;
if (t < 0 || t > 1) return false;
float u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1) return false;
return true;
}
bool BaseCreatureEntity::intersectsTile(Vector2D a1, Vector2D a2, int xTile, int yTile)
{
int posX = xTile * TILE_WIDTH;
int posY = yTile * TILE_HEIGHT;
if (intersectsSegments(a1, a2, Vector2D(posX, posY), Vector2D(posX + TILE_WIDTH, posY))) return true;
else if (intersectsSegments(a1, a2, Vector2D(posX, posY + TILE_HEIGHT), Vector2D(posX + TILE_WIDTH, posY + TILE_HEIGHT))) return true;
else if (intersectsSegments(a1, a2, Vector2D(posX + TILE_WIDTH, posY), Vector2D(posX + TILE_WIDTH, posY + TILE_HEIGHT))) return true;
else if (intersectsSegments(a1, a2, Vector2D(posX, posY), Vector2D(posX, posY + TILE_HEIGHT))) return true;
else
return false;
}
bool BaseCreatureEntity::canSee(float xf, float yf)
{
int tileX0 = x / TILE_WIDTH;
int tileXf = xf / TILE_WIDTH;
int tileY0 = y / TILE_HEIGHT;
int tileYf = yf / TILE_HEIGHT;
int xBegin, xEnd, yBegin, yEnd;
if (tileXf < tileX0)
{
xBegin = tileXf;
xEnd = tileX0;
}
else
{
xBegin = tileX0;
xEnd = tileXf;
}
if (tileYf < tileY0)
{
yBegin = tileYf;
yEnd = tileY0;
}
else
{
yBegin = tileY0;
yEnd = tileYf;
}
for (int i = xBegin; i <= xEnd; i++ )
for (int j = yBegin; j <= yEnd; j++ )
{
if (!game().getCurrentMap()->isShootable(i, j))
if (intersectsTile(Vector2D(x, y), game().getPlayerPosition(), i , j))
return false;
}
return true;
}
bool BaseCreatureEntity::canWalkTo(float xf, float yf)
{
int tileX0 = x / TILE_WIDTH;
int tileXf = xf / TILE_WIDTH;
int tileY0 = y / TILE_HEIGHT;
int tileYf = yf / TILE_HEIGHT;
int xBegin, xEnd, yBegin, yEnd;
if (tileXf < tileX0)
{
xBegin = tileXf;
xEnd = tileX0;
}
else
{
xBegin = tileX0;
xEnd = tileXf;
}
if (tileYf < tileY0)
{
yBegin = tileYf;
yEnd = tileY0;
}
else
{
yBegin = tileY0;
yEnd = tileYf;
}
for (int i = xBegin; i <= xEnd; i++ )
for (int j = yBegin; j <= yEnd; j++ )
{
if (!game().getCurrentMap()->isWalkable(i, j))
if (intersectsTile(Vector2D(x, y), game().getPlayerPosition(), i , j))
return false;
}
return true;
}
void BaseCreatureEntity::heal(int healPoints)
{
int savedHp = hp;
hp += healPoints;
if (hp > hpMax) hp = hpMax;
int healedHp = hp - savedHp;
if (savedHp > 0)
{
std::ostringstream oss;
oss << "+" << healedHp;
int textSize;
if (healedHp < 8) textSize = 17;
else textSize = 17 + (healedHp - 3) / 5;
TextEntity* text = new TextEntity(oss.str(), textSize, x, y - 20.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
while (textTooClose(text, 15, 15)) text->setY(text->getY() - 5);
}
+
+ if (type == ENTITY_PLAYER) game().addHealingStat(healedHp);
}
bool BaseCreatureEntity::isAttacking()
{
return false;
}
diff --git a/src/BaseCreatureEntity.h b/src/BaseCreatureEntity.h
index 6f1e019..10ec8d6 100644
--- a/src/BaseCreatureEntity.h
+++ b/src/BaseCreatureEntity.h
@@ -1,319 +1,405 @@
#ifndef BASECREATUREENTITY_H
#define BASECREATUREENTITY_H
#include "sfml_game/CollidingSpriteEntity.h"
#include "TextEntity.h"
#include "Constants.h"
enum enemyTypeEnum
{
// normal
EnemyTypeBat,
EnemyTypeBatSkeleton,
EnemyTypeRat,
EnemyTypeRatBlack,
EnemyTypeRatHelmet,
EnemyTypeRatBlackHelmet,
EnemyTypeEvilFlower,
EnemyTypeEvilFlowerIce,
EnemyTypeEvilFlowerFire,
EnemyTypeSnake,
EnemyTypeSnakeBlood,
EnemyTypeSlime,
EnemyTypeSlimeRed,
EnemyTypeSlimeBlue,
EnemyTypeSlimeViolet,
EnemyTypeImpBlue,
EnemyTypeImpRed,
EnemyTypePumpkin,
EnemyTypeWitch,
EnemyTypeWitchRed,
EnemyTypeCauldron,
EnemyTypeCauldronElemental,
EnemyTypeSpiderEgg,
EnemyTypeSpiderLittle,
EnemyTypeSpiderTarantula,
EnemyTypeGhost,
EnemyTypeZombie,
EnemyTypeZombieDark,
EnemyTypeBogeyman,
EnemyTypeSlimeLarge,
EnemyTypeSlimeRedLarge,
EnemyTypeSlimeBlueLarge,
EnemyTypeSlimeVioletLarge,
EnemyTypeSausage,
// mini boss
EnemyTypeBubble,
EnemyTypeBubbleIce,
EnemyTypeBubbleGreater,
// boss
EnemyTypeButcher,
EnemyTypeSlimeBoss,
EnemyTypeCyclops,
EnemyTypeRatKing,
EnemyTypeSpiderGiant,
EnemyTypeFrancky,
EnemyTypeVampire,
// invocated
EnemyTypeBat_invocated,
EnemyTypeBatSkeleton_invocated,
EnemyTypeRat_invocated,
EnemyTypeRatGreen,
EnemyTypeRatHelmet_invocated,
EnemyTypeSnake_invocated,
EnemyTypeSnakeBlood_invocated,
EnemyTypeSlime_invocated,
EnemyTypeSlimeRed_invocated,
EnemyTypeSlimeBlue_invocated,
EnemyTypeSlimeViolet_invocated,
EnemyTypePumpkin_invocated,
EnemyTypeSpiderEgg_invocated,
EnemyTypeSpiderLittle_invocated,
EnemyTypeSpiderTarantula_invocated,
EnemyTypeZombie_invocated,
EnemyTypeSausage_invocated,
EnemyTypeRockFalling,
EnemyTypeRockMissile,
EnemyTypeSpiderWeb,
EnemyTypeFranckyHead,
EnemyTypeFranckyHand,
EnemyTypeFranckyFoot,
EnemyTypeVampireDead,
EnemyTypeNone, // player of fairy
EnemyTypeDestroyable,
NB_ENEMY // = no enemy
};
+const std::vector<std::string> enemyString =
+{
+ // normal
+ "EnemyTypeBat",
+ "EnemyTypeBatSkeleton",
+ "EnemyTypeRat",
+ "EnemyTypeRatBlack",
+ "EnemyTypeRatHelmet",
+ "EnemyTypeRatBlackHelmet",
+ "EnemyTypeEvilFlower",
+ "EnemyTypeEvilFlowerIce",
+ "EnemyTypeEvilFlowerFire",
+ "EnemyTypeSnake",
+ "EnemyTypeSnakeBlood",
+ "EnemyTypeSlime",
+ "EnemyTypeSlimeRed",
+ "EnemyTypeSlimeBlue",
+ "EnemyTypeSlimeViolet",
+ "EnemyTypeImpBlue",
+ "EnemyTypeImpRed",
+ "EnemyTypePumpkin",
+ "EnemyTypeWitch",
+ "EnemyTypeWitchRed",
+ "EnemyTypeCauldron",
+ "EnemyTypeCauldronElemental",
+ "EnemyTypeSpiderEgg",
+ "EnemyTypeSpiderLittle",
+ "EnemyTypeSpiderTarantula",
+ "EnemyTypeGhost",
+ "EnemyTypeZombie",
+ "EnemyTypeZombieDark",
+ "EnemyTypeBogeyman",
+ "EnemyTypeSlimeLarge",
+ "EnemyTypeSlimeRedLarge",
+ "EnemyTypeSlimeBlueLarge",
+ "EnemyTypeSlimeVioletLarge",
+ "EnemyTypeSausage",
+
+ // mini boss
+ "EnemyTypeBubble",
+ "EnemyTypeBubbleIce",
+ "EnemyTypeBubbleGreater",
+
+ // boss
+ "EnemyTypeButcher",
+ "EnemyTypeSlimeBoss",
+ "EnemyTypeCyclops",
+ "EnemyTypeRatKing",
+ "EnemyTypeSpiderGiant",
+ "EnemyTypeFrancky",
+ "EnemyTypeVampire",
+
+ // invocated
+ "EnemyTypeBat_invocated",
+ "EnemyTypeBatSkeleton_invocated",
+ "EnemyTypeRat_invocated",
+ "EnemyTypeRatGreen",
+ "EnemyTypeRatHelmet_invocated",
+ "EnemyTypeSnake_invocated",
+ "EnemyTypeSnakeBlood_invocated",
+ "EnemyTypeSlime_invocated",
+ "EnemyTypeSlimeRed_invocated",
+ "EnemyTypeSlimeBlue_invocated",
+ "EnemyTypeSlimeViolet_invocated",
+ "EnemyTypePumpkin_invocated",
+ "EnemyTypeSpiderEgg_invocated",
+ "EnemyTypeSpiderLittle_invocated",
+ "EnemyTypeSpiderTarantula_invocated",
+ "EnemyTypeZombie_invocated",
+ "EnemyTypeSausage_invocated",
+
+ "EnemyTypeRockFalling",
+ "EnemyTypeRockMissile",
+ "EnemyTypeSpiderWeb",
+
+ "EnemyTypeFranckyHead",
+ "EnemyTypeFranckyHand",
+ "EnemyTypeFranckyFoot",
+
+ "EnemyTypeVampireDead",
+
+ "EnemyTypeNone", // player of fairy
+ "EnemyTypeDestroyable",
+};
+
enum sourceTypeEnum
{
SourceTypeMelee,
SourceTypeBolt,
SourceTypeExplosion,
SourceTypePoison
};
struct StructHurt
{
int damage;
enumShotType hurtingType;
int level;
bool critical;
sourceTypeEnum sourceType;
enemyTypeEnum enemyType;
bool goThrough;
};
enum enumSpecialState
{
SpecialStateIce, // = 0
SpecialStateSlow, // param 1 = multiplier
SpecialStateSpeed, // param 1 = multiplier
SpecialStatePoison,
SpecialStateWeakness,
SpecialStateStrength,
SpecialStateRage,
SpecialStateConfused,
SpecialStateTime,
DivineStateProtection,
DivineStateSpeed,
DivineStateFireRate,
DivineStateFireDamage,
NB_SPECIAL_STATES
};
enum enumStateResistance { ResistanceImmune, ResistanceVeryHigh, ResistanceHigh, ResistanceStandard, ResistanceLow, ResistanceVeryLow};
enum enumResistances
{
ResistanceIce, // = 0
ResistanceFire,
ResistanceStone,
ResistanceLightning,
ResistanceIllusion,
ResistancePoison,
- ResistanceRecoil,
+ ResistanceRepulsion,
ResistanceFrozen,
NB_RESISTANCES
};
struct specialStateStuct
{
enumSpecialState type;
bool active;
float timer;
float param1;
float param2;
float param3;
bool waitUnclear;
};
const std::string specialStateToLabel[NB_SPECIAL_STATES] =
{
"state_frozen",
"state_slow",
"state_speed",
"state_poison",
"state_weakness",
"state_strength",
"state_rage",
"state_confusion",
"state_time",
"state_div_protection",
"state_div_speed",
"state_div_firerate",
"state_div_damage"
};
const sf::Color specialStateToColor[NB_SPECIAL_STATES] =
{
sf::Color(100, 200, 255),
sf::Color::Red,
sf::Color::White,
sf::Color::Green,
sf::Color::Red,
sf::Color::White,
sf::Color::White,
sf::Color::White,
sf::Color::Red,
sf::Color::Yellow,
sf::Color::Yellow,
sf::Color::Yellow,
sf::Color::Yellow
};
class BaseCreatureEntity : public CollidingSpriteEntity
{
public:
BaseCreatureEntity(sf::Texture* image, float x, float y, int spriteWidth, int spriteHeight);
int getHp();
int getHpMax();
void setHp(int hp);
void setHpMax(int hpMax);
int getHpDisplay();
float getCreatureSpeed();
IntCoord getCurrentTile();
virtual void animate(float delay);
virtual float animateStates(float delay);
virtual void animateColors(float delay);
virtual void animateRecoil(float delay);
virtual void animatePhysics(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
virtual bool collideWithMap(int direction);
void displayFlyingText(float xText, float yText, int sizeText, std::string text, TextEntity::colorTypeEnum color);
virtual int hurt(StructHurt hurtParam);
virtual void prepareDying();
virtual void dying();
enum enumMovingStyle { movWalking, movFlying};
virtual enumMovingStyle getMovingStyle();
enum enumBloodColor
{
BloodNone = -1,
BloodRed,
BloodGreen,
BloodRock,
BloodEgg,
BloodBubble,
BloodBubbleIce,
BloodBlack,
BloodBarrel,
BloodBarrelPowder,
BloodSkull
};
specialStateStuct specialState[NB_SPECIAL_STATES];
virtual void setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2, bool waitUnclear = false);
enumStateResistance resistance[NB_RESISTANCES];
bool isSpecialStateActive(enumSpecialState state);
specialStateStuct getSpecialState(enumSpecialState state);
- virtual void giveRecoil(bool stun, Vector2D velocity, float timer);
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void giveRepulsion(bool stun, Vector2D velocity, float timer);
+ virtual void inflictsRepulsionTo(BaseCreatureEntity* targetEntity);
virtual void computeFacingDirection();
virtual void dyingFromAge();
virtual bool canCollide();
bool canSee(float xf, float yf);
bool canWalkTo(float xf, float yf);
void heal(int healPoints);
static StructHurt getHurtParams(int damage,
enumShotType hurtingType,
int level,
bool critical,
sourceTypeEnum sourceType,
enemyTypeEnum enemyType,
bool goThrough)
{
StructHurt hurtParams;
hurtParams.damage = damage;
hurtParams.hurtingType = hurtingType;
hurtParams.level = level;
hurtParams.critical = critical;
hurtParams.sourceType = sourceType;
hurtParams.enemyType = enemyType;
hurtParams.goThrough = goThrough;
return hurtParams;
}
bool intersectsSegments(Vector2D a1, Vector2D a2, Vector2D b1, Vector2D b2);
bool intersectsTile(Vector2D a1, Vector2D a2, int xTile, int yTile);
virtual bool isAttacking(); // true when the monster is performing a melee attack
protected:
int hp;
int hpMax;
int hpDisplay;
float creatureSpeed;
bool doesAccelerate;
int shadowFrame;
int facingDirection;
float armor;
bool canExplode; // true if the monster can explode with a fire attack
bool displayDamage;
bool hurting;
float hurtingDelay;
enumShotType hurtingType;
enumBloodColor bloodColor;
enumMovingStyle movingStyle;
- struct recoilStruct
+ struct RepulseStruct
{
bool active;
Vector2D velocity;
bool stun;
+ bool propage;
float timer;
- } recoil;
+ } repulsion;
void generateStar(sf::Color starColor);
virtual void makeExplode();
private:
bool determineSatusChance(enumStateResistance resistance, int level);
int determineDamageBonus(enumStateResistance resistance, int level);
bool textTooClose(TextEntity* textEntity, float xDistMin, float yDistMin);
};
#endif // BASECREATUREENTITY_H
diff --git a/src/BatEntity.cpp b/src/BatEntity.cpp
index 05b5b7a..fc782d1 100644
--- a/src/BatEntity.cpp
+++ b/src/BatEntity.cpp
@@ -1,156 +1,156 @@
#include "BatEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
const int BAT_ACCELERATION = 10;
BatEntity::BatEntity(float x, float y, EnumBatType batType, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_BAT), x, y)
{
creatureSpeed = BAT_SPEED;
velocity = Vector2D(creatureSpeed);
imagesProLine = 10;
this->invocated = invocated;
this->batType = batType;
changingDelay = -0.5f;
shadowFrame = 9;
movingStyle = movFlying;
agonizingSound = SOUND_BAT_DYING;
sprite.setOrigin(32.0f, 26.0f);
if (batType == BatStandard)
{
enemyType = invocated ? EnemyTypeBat_invocated : EnemyTypeBat;
hp = BAT_HP;
meleeDamages = BAT_DAMAGES;
bloodColor = BloodRed;
dyingFrame = 8;
deathFrame = FRAME_CORPSE_BAT;
}
else // BatSkeleton
{
enemyType = invocated ? EnemyTypeBatSkeleton_invocated : EnemyTypeBatSkeleton;
hp = BAT_HP * 2;
meleeDamages = 8;
bloodColor = BloodNone;
dyingFrame = 18;
deathFrame = FRAME_CORPSE_BAT_SKELETON;
}
}
void BatEntity::animate(float delay)
{
if (!isAgonising)
{
changingDelay -= delay;
if (changingDelay < 0.0f)
{
if (batType != BatSkeleton || rand() % 3 == 0)
{
velocity = Vector2D(creatureSpeed);
acceleration.x = velocity.x / BAT_ACCELERATION;
acceleration.y = velocity.y / BAT_ACCELERATION;
computeFacingDirection();
velocity = Vector2D {0, 0};
doesAccelerate = true;
}
else
{
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
acceleration.x = velocity.x / BAT_ACCELERATION;
acceleration.y = velocity.y / BAT_ACCELERATION;
computeFacingDirection();
velocity = Vector2D {0, 0};
doesAccelerate = true;
}
changingDelay = 0.5f + (float)(rand() % 2500) / 1000.0f;
}
if (age < 0.0f)
frame = 1;
else
{
switch (facingDirection)
{
case 2: frame = 0; break;
case 4: frame = 2; break;
case 6: frame = 4; break;
case 8: frame = 6; break;
}
frame += ((int)(age * 7.0f)) % 2;
}
}
if (batType == BatSkeleton) frame += 10;
EnemyEntity::animate(delay);
}
void BatEntity::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 10;
boundingBox.height = 25;
}
void BatEntity::collideMapRight()
{
velocity.x = -velocity.x;
acceleration.x = -acceleration.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
else computeFacingDirection();
}
void BatEntity::collideMapLeft()
{
velocity.x = -velocity.x;
acceleration.x = -acceleration.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
else computeFacingDirection();
}
void BatEntity::collideMapTop()
{
velocity.y = -velocity.y;
acceleration.y = -acceleration.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
else computeFacingDirection();
}
void BatEntity::collideMapBottom()
{
velocity.y = -velocity.y;
acceleration.y = -acceleration.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
else computeFacingDirection();
}
void BatEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getEnemyType() != EnemyTypeSpiderWeb && entity->getMovingStyle() == movFlying)
{
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), BAT_SPEED ));
computeFacingDirection();
}
}
void BatEntity::dying()
{
EnemyEntity::dying();
h = 25.0f;
}
void BatEntity::drop()
{
if (!invocated) EnemyEntity::drop();
}
diff --git a/src/BlackRatEntity.cpp b/src/BlackRatEntity.cpp
index e099988..d51d931 100644
--- a/src/BlackRatEntity.cpp
+++ b/src/BlackRatEntity.cpp
@@ -1,310 +1,310 @@
#include "BlackRatEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
BlackRatEntity::BlackRatEntity(float x, float y, ratBlackTypeEnum ratType)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_RAT), x, y),
currentTile(0, 0),
targetTile(0, 0)
{
this->ratType = ratType;
imagesProLine = 10;
if (ratType == RatBlackTypeNormal)
{
frame = 21;
dyingFrame = 29;
deathFrame = FRAME_CORPSE_BLACK_RAT;
enemyType = EnemyTypeRatBlack;
hp = BLACK_RAT_HP;
creatureSpeed = BLACK_RAT_SPEED;
}
else //(ratType == RatBlackTypeHelmet)
{
frame = 41;
dyingFrame = 49;
deathFrame = FRAME_CORPSE_BLACK_RAT_HELMET;
enemyType = EnemyTypeRatBlackHelmet;
hp = BLACK_RAT_HP_HELMET;
creatureSpeed = BLACK_RAT_SPEED_HELMET;
}
meleeDamages = BLACK_RAT_DAMAGES;
bloodColor = BloodRed;
shadowFrame = -1;
agonizingSound = SOUND_RAT_DYING;
currentDirection = 0;
sprite.setOrigin(32.0f, 38.0f);
findNextGoal();
}
void BlackRatEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
// goal reached ?
if (currentDirection == 6 && x > (targetTile.x * TILE_WIDTH + TILE_WIDTH / 2) ) findNextGoal();
else if (currentDirection == 4 && x < (targetTile.x * TILE_WIDTH + TILE_WIDTH / 2) ) findNextGoal();
else if (currentDirection == 2 && y > (targetTile.y * TILE_HEIGHT + TILE_HEIGHT / 2 - 5) ) findNextGoal();
else if (currentDirection == 8 && y < (targetTile.y * TILE_HEIGHT + TILE_HEIGHT / 2 - 5) ) findNextGoal();
checkNextFacing(delay);
frame = ((int)(age * 5.0f)) % 4;
if (frame == 3) frame = 1;
if (facingDirection == 4 || facingDirection == 6) frame += 3;
isMirroring = (facingDirection == 6 );
if (facingDirection == 8) frame += 6;
if (ratType == RatBlackTypeHelmet) frame += 40;
else frame += 20;
}
EnemyEntity::animate(delay);
z = y + 17;
}
void BlackRatEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + RAT_BB_LEFT;
boundingBox.width = width - RAT_BB_WIDTH_DIFF;
boundingBox.top = (int)y - 13;
boundingBox.height = 31;
}
void BlackRatEntity::collideMapRight()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
findNextGoal();
}
void BlackRatEntity::collideMapLeft()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
findNextGoal();
}
void BlackRatEntity::collideMapTop()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
findNextGoal();
}
void BlackRatEntity::collideMapBottom()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
findNextGoal();
}
void BlackRatEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
int oldDirection = currentDirection;
if (currentDirection == 6 && entity->getX() > x)
{
currentDirection = 4;
targetTile = IntCoord(currentTile.x - 1, currentTile.y);
}
else if (currentDirection == 4 && entity->getX() < x)
{
currentDirection = 6;
targetTile = IntCoord(currentTile.x + 1, currentTile.y);
}
else if (currentDirection == 8 && entity->getY() < y)
{
currentDirection = 2;
targetTile = IntCoord(currentTile.x, currentTile.y + 1);
}
else if (currentDirection == 2 && entity->getY() > y)
{
currentDirection = 8;
targetTile = IntCoord(currentTile.x, currentTile.y - 1);
}
if (oldDirection != currentDirection)
{
switch (currentDirection)
{
case 4: velocity.x = - creatureSpeed; velocity.y = 0.0f; break;
case 6: velocity.x = + creatureSpeed; velocity.y = 0.0f; break;
case 2: velocity.y = + creatureSpeed; velocity.x = 0.0f; break;
case 8: velocity.y = - creatureSpeed; velocity.x = 0.0f; break;
default: break;
}
acceleration = { 0, 0 };
nextFacingDirection = currentDirection;
}
}
}
void BlackRatEntity::findNextGoal()
{
currentTile = getCurrentTile();
DungeonMap* dMap = game().getCurrentMap();
int backDirection = 0;
int oldDirection = currentDirection;
switch (currentDirection)
{
case 4: backDirection = 6; break;
case 6: backDirection = 4; break;
case 2: backDirection = 8; break;
case 8: backDirection = 2; break;
default: break;
}
bool ok = false;
{
int r = 0;
while (!ok)
{
r++;
if (r == 150) // watchdog
ok = true;
else if (r == 40)
{
backDirection = 5;
}
int newDir = rand() % 4;
if (newDir == 0)
{
if (backDirection != 4 && currentTile.x > 1 && (currentTile.y % 2 != 0) && dMap->isWalkable(currentTile.x - 1, currentTile.y))
{
currentDirection = 4;
targetTile = IntCoord(currentTile.x - 1, currentTile.y);
ok = true;
}
}
else if (newDir == 1)
{
if (backDirection != 6 && currentTile.x < MAP_WIDTH - 2 && (currentTile.y % 2 != 0) && dMap->isWalkable(currentTile.x + 1, currentTile.y))
{
currentDirection = 6;
targetTile = IntCoord(currentTile.x + 1, currentTile.y);
ok = true;
}
}
else if (newDir == 2)
{
if (backDirection != 8 && currentTile.y > 1 && (currentTile.x % 2 != 0) && dMap->isWalkable(currentTile.x, currentTile.y - 1))
{
currentDirection = 8;
targetTile = IntCoord(currentTile.x, currentTile.y - 1);
ok = true;
}
}
else
{
if (backDirection != 2 && currentTile.y < MAP_HEIGHT - 2 && (currentTile.x % 2 != 0) && dMap->isWalkable(currentTile.x, currentTile.y + 1))
{
currentDirection = 2;
targetTile = IntCoord(currentTile.x, currentTile.y + 1);
ok = true;
}
}
}
}
float accelerationAbs = (enemyType == EnemyTypeRatBlackHelmet) ? (creatureSpeed / 8) : (creatureSpeed / 16);
switch (currentDirection)
{
case 4:
velocity.x = -creatureSpeed;
velocity.y = 0.0f;
acceleration.x = -accelerationAbs;
acceleration.y = 0.0f;
break;
case 6:
velocity.x = creatureSpeed;
velocity.y = 0.0f;
acceleration.x = accelerationAbs;
acceleration.y = 0;
break;
case 2:
velocity.y = creatureSpeed;
velocity.x = 0.0f;
acceleration.y = accelerationAbs;
acceleration.x = 0.0f;
break;
case 8:
velocity.y = -creatureSpeed;
velocity.x = 0.0f;
acceleration.y = -accelerationAbs;
acceleration.x = 0.0f;
break;
default: break;
}
if (oldDirection != currentDirection)
{
velocity = Vector2D {0, 0};
doesAccelerate = true;
}
nextFacingDirection = currentDirection;
}
void BlackRatEntity::collideWithBolt(BoltEntity* boltEntity)
{
if (ratType == RatBlackTypeHelmet && boltEntity->getBoltType() != ShotTypeIllusion)
{
int collisionDir = getCollisionDirection(boltEntity);
bool boltCollide = true;
switch (facingDirection)
{
case 4:
if (collisionDir == 7 || collisionDir == 4 || collisionDir == 1) boltCollide = false;
break;
case 2:
if (collisionDir == 1 || collisionDir == 2 || collisionDir == 3) boltCollide = false;
break;
case 6:
if (collisionDir == 9 || collisionDir == 6 || collisionDir == 3) boltCollide = false;
break;
case 8:
if (collisionDir == 7 || collisionDir == 8 || collisionDir == 9) boltCollide = false;
break;
}
if (boltCollide) EnemyEntity::collideWithBolt(boltEntity);
else
{
float xs = (x + boltEntity->getX()) / 2;
float ys = (y + boltEntity->getY()) / 2;
boltEntity->collide();
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
SoundManager::getInstance().playSound(SOUND_CLANG_00);
boltEntity->loseDamages(boltEntity->getDamages());
if (boltEntity->getBoltType() == ShotTypeStone)
{
float factor = (boltEntity->isFromPlayer() && game().getPlayer()->isEquiped(EQUIP_RAPID_SHOT)) ? 0.25f : 1.0f;
float recoilVelocity = factor * STONE_DECOIL_VELOCITY[boltEntity->getLevel()];
float recoilDelay = factor * STONE_DECOIL_DELAY[boltEntity->getLevel()];
Vector2D recoilVector = Vector2D(0, 0).vectorTo(boltEntity->getVelocity(),
recoilVelocity );
- giveRecoil(true, recoilVector, recoilDelay);
+ giveRepulsion(true, recoilVector, recoilDelay);
}
}
}
else EnemyEntity::collideWithBolt(boltEntity);
}
diff --git a/src/BogeymanEntity.cpp b/src/BogeymanEntity.cpp
index 1cb1e19..59de45f 100644
--- a/src/BogeymanEntity.cpp
+++ b/src/BogeymanEntity.cpp
@@ -1,310 +1,310 @@
#include "BogeymanEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "EnemyBoltEntity.h"
const float BOGEY_APPARITION_TIME = 1.0f;
const float BOGEY_DISAPPEARING_TIME = 1.0f;
const float BOGEY_ANIM_SPEED = 0.4f;
const float BOGEY_MOVING_DELAY = 1.0f;
const float BOGEY_INVISIBLE_DELAY = 1.0f;
const float BOGEY_VELOCITY = 155;
const float BOGEY_FIRE_VELOCITY = 320.0f;
const int BOGEY_HP = 100;
BogeymanEntity::BogeymanEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_BOGEY), x, y)
{
// general
creatureSpeed = BOGEY_VELOCITY;
velocity = Vector2D(0.0f);
hp = BOGEY_HP;
meleeDamages = IMP_MELEE_DAMAGES;
bloodColor = BloodBlack;
changingDelay = BOGEY_APPARITION_TIME;
shadowFrame = -1;
movingStyle = movFlying;
imagesProLine = 8;
height = 88;
sprite.setOrigin(32,80);
state = 0;
resistance[ResistanceFrozen] = ResistanceImmune;
resistance[ResistancePoison] = ResistanceImmune;
resistance[ResistanceIce] = ResistanceHigh;
resistance[ResistanceIllusion] = ResistanceHigh;
resistance[ResistanceLightning] = ResistanceLow;
enemyType = EnemyTypeBogeyman;
hasFired = false;
isFireType1 = rand() % 2 == 0;
age = -0.001f * (rand()%4000);
}
void BogeymanEntity::animate(float delay)
{
if (!isDying && !isAgonising)
{
if (age < 0.0f)
frame = 1;
else
{
changingDelay -= delay;
if (changingDelay < 0.0f)
{
if (state == 0) // appear -> attack
{
state = 1;
changingDelay = 2 * BOGEY_ANIM_SPEED;
hasFired = false;
SoundManager::getInstance().playSound(SOUND_BOGEYMAN_ATTACK);
}
else if (state == 1) // attack -> move
{
changingDelay = BOGEY_MOVING_DELAY;
state = 2;
}
else if (state == 2) // move -> disappear
{
state = 3;
changingDelay = BOGEY_DISAPPEARING_TIME;
setVelocity(Vector2D(0.0f, 0.0f));
SoundManager::getInstance().playSound(SOUND_BOGEYMAN_VORTEX_01);
}
else if (state == 3)
{
state = 4;
changingDelay = BOGEY_INVISIBLE_DELAY;
}
else if (state == 4) // invisible -> apparition
{
state = 0;
changingDelay = BOGEY_APPARITION_TIME;
teleport();
SoundManager::getInstance().playSound(SOUND_BOGEYMAN_VORTEX_00);
}
else if (state == -1) // dying
{
isDying = true;
}
}
if (state == 1) // fira anim
{
frame = changingDelay / BOGEY_ANIM_SPEED * 8;
if (!hasFired && frame < 8)
{
hasFired = true;
fire();
}
frame = 32 - frame;
}
else if (state == 2) // normal
{
frame = (int)((BOGEY_MOVING_DELAY - changingDelay) / BOGEY_ANIM_SPEED * 8) % 16;
}
else if (state == -1)
{
frame = 64 - (8 * (changingDelay / BOGEY_ANIM_SPEED));
}
}
}
EnemyEntity::animate(delay);
if (age > 0 && delay >= age) SoundManager::getInstance().playSound(SOUND_BOGEYMAN_VORTEX_00);
z = y + 8;
}
void BogeymanEntity::calculateBB()
{
boundingBox.left = (int)x - 14;
boundingBox.width = 28;
boundingBox.top = (int)y - 55;
boundingBox.height = 58;
}
void BogeymanEntity::collideMapRight()
{
velocity.x = -velocity.x;
}
void BogeymanEntity::collideMapLeft()
{
velocity.x = -velocity.x;
}
void BogeymanEntity::collideMapTop()
{
velocity.y = -velocity.y;
}
void BogeymanEntity::collideMapBottom()
{
velocity.y = -velocity.y;
}
void BogeymanEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getEnemyType() != EnemyTypeSpiderWeb && entity->getMovingStyle() == movFlying)
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 50.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
}
}
bool BogeymanEntity::canCollide()
{
return (state == 1 || state == 2);
}
int BogeymanEntity::hurt(StructHurt hurtParam)
{
int result = EnemyEntity::hurt(hurtParam);
return result;
}
void BogeymanEntity::dying()
{
EnemyEntity::dying();
for (int i = 0; i < 4; i++) game().generateBlood(x, y, BloodBlack);
h = 25.0f;
}
void BogeymanEntity::fire()
{
for (int i = 0; i < 4; i++)
{
EnemyBoltEntity* bolt;
bolt = new EnemyBoltEntity(x, y - 40, ShotTypeIllusion, 0, enemyType);
bolt->setDamages(8);
if (isFireType1)
{
switch (i)
{
case 0: bolt->setVelocity(Vector2D(BOGEY_FIRE_VELOCITY * 0.7f, BOGEY_FIRE_VELOCITY * 0.7f)); break;
case 1: bolt->setVelocity(Vector2D(BOGEY_FIRE_VELOCITY * -0.7f, BOGEY_FIRE_VELOCITY * 0.7f)); break;
case 2: bolt->setVelocity(Vector2D(BOGEY_FIRE_VELOCITY * -0.7f, BOGEY_FIRE_VELOCITY * -0.7f)); break;
case 3: bolt->setVelocity(Vector2D(BOGEY_FIRE_VELOCITY * 0.7f, BOGEY_FIRE_VELOCITY * -0.7f)); break;
}
}
else
{
switch (i)
{
case 0: bolt->setVelocity(Vector2D(BOGEY_FIRE_VELOCITY, 0)); break;
case 1: bolt->setVelocity(Vector2D(-BOGEY_FIRE_VELOCITY, 0)); break;
case 2: bolt->setVelocity(Vector2D(0, BOGEY_FIRE_VELOCITY)); break;
case 3: bolt->setVelocity(Vector2D(0, -BOGEY_FIRE_VELOCITY)); break;
}
}
}
SoundManager::getInstance().playSound(SOUND_BLAST_ILLUSION);
isFireType1 = !isFireType1;
}
void BogeymanEntity::render(sf::RenderTarget* app)
{
if (hurting && hp > 0)
{
if (hurtingDelay > 0.0f)
{
int fadeColor = (sf::Uint8)((HURTING_DELAY - hurtingDelay) * 127);
if (hurtingDelay > HURTING_DELAY) fadeColor = 0;
sprite.setColor(sf::Color(255, 255, 255, 128 + fadeColor )); // fade
}
}
if (state == 0)
{
if (changingDelay > BOGEY_APPARITION_TIME - 0.5f)
{
float scale = 2 * (BOGEY_APPARITION_TIME - changingDelay);
sprite.setScale(scale, scale);
}
else if (changingDelay < 0.5f)
{
float scale = changingDelay * 2;
sprite.setScale(scale, scale);
}
else
sprite.setScale(1.0f, 1.0f);
int holeFrame = ((int)(age * 20.0f)) % 8;
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(holeFrame * width, 5 * height, width, height));
app->draw(sprite);
sprite.setScale(1.0f, 1.0f);
if (changingDelay <= BOGEY_ANIM_SPEED)
{
frame = 32 + (8 * (changingDelay / BOGEY_ANIM_SPEED));
EnemyEntity::render(app);
}
}
else if (state == 3)
{
if (changingDelay > BOGEY_DISAPPEARING_TIME - 0.5f)
{
float scale = 2 * (BOGEY_DISAPPEARING_TIME - changingDelay);
sprite.setScale(scale, scale);
}
else if (changingDelay < 0.5f)
{
float scale = changingDelay * 2;
sprite.setScale(scale, scale);
}
else
sprite.setScale(1.0f, 1.0f);
int holeFrame = ((int)(age * 12.0f)) % 8;
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(holeFrame * width, 5 * height, width, height));
app->draw(sprite);
sprite.setScale(1.0f, 1.0f);
if (changingDelay >= (BOGEY_DISAPPEARING_TIME - BOGEY_ANIM_SPEED))
{
frame = 32 + (8 * ((BOGEY_DISAPPEARING_TIME - changingDelay) / BOGEY_ANIM_SPEED));
EnemyEntity::render(app);
}
}
else if (state != 4)
EnemyEntity::render(app);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void BogeymanEntity::teleport()
{
x = ((int)(game().getPlayer()->getX()) / TILE_WIDTH) * TILE_WIDTH + TILE_WIDTH * 0.5f;
y = ((int)(game().getPlayer()->getY()) / TILE_HEIGHT) * TILE_HEIGHT + TILE_HEIGHT * 0.5f;
}
void BogeymanEntity::prepareDying()
{
state = -1; // dying
changingDelay = BOGEY_ANIM_SPEED * 2;
velocity.x = 0.0f;
velocity.y = 0.0f;
SoundManager::getInstance().playSound(SOUND_BOGEYMAN_DIE);
type = ENTITY_ENEMY_NC;
}
diff --git a/src/BoltEntity.cpp b/src/BoltEntity.cpp
index b6ac770..3caf9b4 100644
--- a/src/BoltEntity.cpp
+++ b/src/BoltEntity.cpp
@@ -1,416 +1,522 @@
#include "BoltEntity.h"
#include "ExplosionEntity.h"
+#include "EffectZoneEntity.h"
#include "Constants.h"
#include "DungeonMap.h"
#include "WitchBlastGame.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
BoltEntity::BoltEntity(float x, float y, float boltLifeTime, enumShotType boltType, int level)
: CollidingSpriteEntity (ImageManager::getInstance().getImage(IMAGE_BOLT), x, y, BOLT_WIDTH, BOLT_HEIGHT)
{
lifetime = boltLifeTime;
setDamages(INITIAL_BOLT_DAMAGES);
type = ENTITY_BOLT;
viscosity = INITIAL_BOLT_VISCOSITY;
this->level = level;
if (boltType == ShotTypeLightning) viscosity += LIGHTNING_VISCOSITY_INCREASE[level];
frame = 0;
setMap(game().getCurrentMap(), TILE_WIDTH, TILE_HEIGHT, 0, 0);
this->boltType = boltType;
enemyType = EnemyTypeNone;
goThrough = false;
hitNumber = 0;
fromPlayer = true;
switch (boltType)
{
case ShotTypeDeterministic:
case ShotTypeStandard: frame = 0; break;
case ShotTypeCold:
case ShotTypeIce: frame = 2; break;
case ShotTypeStone: frame = 4; break;
case ShotTypeLightning: frame = 5; goThrough = true; break;
- case ShotTypeIllusion: frame = 3; break;
+ case ShotTypeIllusion: frame = 3; goThrough = level >= 2; break;
case ShotTypeFire: frame = 6; break;
case ShotTypePoison: frame = 7; break;
case ShotTypeBomb: frame = 8; damages = 0; sprite.setScale(1.0f, 1.0f); break;
}
testWallsCollision = false;
flying = false;
critical = false;
// avoid starting in wall
if (y > ((MAP_HEIGHT - 1) * TILE_HEIGHT - 16))
this->y = (MAP_HEIGHT - 1) * TILE_HEIGHT - 16;
}
int BoltEntity::getDamages()
{
return damages;
}
void BoltEntity::setFromPlayer(bool fromPlayer)
{
this->fromPlayer = fromPlayer;
}
bool BoltEntity::isFromPlayer()
{
return fromPlayer;
}
unsigned int BoltEntity::getLevel()
{
return level;
}
void BoltEntity::setDamages(int damages)
{
this->damages = damages;
if (damages <= 4) renderScale = 0.7f;
else if (damages <= 6) renderScale = 0.78f;
else if (damages <= 8) renderScale = 0.85f;
else if (damages <= 12) renderScale = 0.9f;
else if (damages <= 16) renderScale = 1.0f;
else if (damages <= 20) renderScale = 1.1f;
else if (damages <= 24) renderScale = 1.2f;
else if (damages <= 30) renderScale = 1.3f;
else renderScale = 1.4f;
sprite.scale(renderScale, renderScale);
}
void BoltEntity::loseDamages(int damages)
{
if (this->damages > damages) setDamages(this->damages - damages);
else setDamages(0);
critical = false;
}
enumShotType BoltEntity::getBoltType()
{
return boltType;
}
enemyTypeEnum BoltEntity::getEnemyType()
{
return enemyType;
}
bool BoltEntity::isFlying()
{
return flying;
}
void BoltEntity::setFlying(bool flying)
{
this->flying = flying;
}
bool BoltEntity::isCritical()
{
return critical;
}
void BoltEntity::setCritical(bool critical)
{
this->critical = critical;
}
void BoltEntity::setGoThrough(bool goThrough)
{
this->goThrough = goThrough;
}
bool BoltEntity::getGoThrough()
{
return goThrough;
}
void BoltEntity::animate(float delay)
{
if (boltType != ShotTypeBomb)
particleGenerator.GenerateParticles(frame, IMAGE_BOLT, x, y, BOLT_WIDTH, BOLT_HEIGHT,
boltType == ShotTypeLightning ? Vector2D(20.0f) : Vector2D(0.0f, 0.0f),
10, boltType == ShotTypeIce ? renderScale * 1.3f : renderScale);
z = y + height;
testWallsCollision = true;
if (isCollidingWithMap())
{
- isDying = true;
+ onDying();
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
for (int i=0; i<5; i++) generateParticule(100.0f + rand() % 150);
}
else
{
float oldX = x;
float oldY = y;
CollidingSpriteEntity::animate(delay);
if (boltType != ShotTypeBomb && (game().getPlayer()->isEquiped(EQUIP_RAPID_SHOT) || damages < 5))
particleGenerator.GenerateParticles(frame, IMAGE_BOLT, (x + oldX) * 0.5f, (y + oldY) * 0.5f, BOLT_WIDTH, BOLT_HEIGHT,
boltType == ShotTypeLightning ? Vector2D(20.0f) : Vector2D(0.0f, 0.0f),
10, boltType == ShotTypeIce ? renderScale * 1.3f : renderScale);
}
// key room collision
if (game().getCurrentMap()->getRoomType() == roomTypeKey && !game().getCurrentMap()->isCleared())
{
sf::IntRect col1;
col1.width = 198;
col1.height = 68;
col1.top = 254;
col1.left = 380;
sf::IntRect col2;
col2.width = 68;
col2.height = 198;
col2.top = 189;
col2.left = 445;
if (boundingBox.intersects(col1) || boundingBox.intersects(col2))
{
game().activateKeyRoomEffect(false);
if (x < 390) collideMapRight();
else if (x > 565) collideMapLeft();
else if (y < 265) collideMapBottom();
else collideMapTop();
}
}
testWallsCollision = false;
calculateBB();
if (boltType != ShotTypeBomb && boltType != ShotTypeStone && (lifetime - age) < 0.2f)
{
if (age >= lifetime)
sprite.setColor(sf::Color(255, 255, 255, 0));
else
sprite.setColor(sf::Color(255, 255, 255, (sf::Uint8)((lifetime - age) / 0.2f * 255)));
}
- if (((velocity.x)*(velocity.x) + (velocity.y)*(velocity.y)) < 1500.0f) isDying = true;
+ if (((velocity.x)*(velocity.x) + (velocity.y)*(velocity.y)) < 1500.0f) onDying();
}
void BoltEntity::render(sf::RenderTarget* app)
{
if (boltType == ShotTypeBomb)
{
sprite.setTextureRect(sf::IntRect(8 * width, 0, width * 2, height * 2));
sprite.setPosition(x - width / 2, y - height);
app->draw(sprite);
}
else
CollidingSpriteEntity::render(app);
if (game().getShowLogical())
{
displayBoundingBox(app);
}
}
void BoltEntity::calculateBB()
{
int colSize = testWallsCollision ? 1 : 10;
boundingBox.left = x - colSize;
boundingBox.width = colSize * 2;
boundingBox.top = y - colSize;
boundingBox.height = colSize * 2;
}
void BoltEntity::collide()
{
hitNumber++;
if (fromPlayer)
{
if (hitNumber == 4) game().registerAchievement(Achievement4Hits);
if (goThrough)
{
- if (damages > 0) return;
+ if (damages > 0)
+ {
+ if (damages > 1 && boltType == ShotTypeLightning && level == 2)
+ {
+ // bolt lvl3 effect
+ if (damages <= 2) damages += 4;
+ else if (damages <= 5) damages += 3;
+ else damages += 2;
+
+ for (int i = 0; i < 3; i++)
+ {
+ BoltEntity* bolt = new BoltEntity(x, y, lifetime, ShotTypeLightning, 1);
+ bolt->setDamages(damages / 2);
+ float shotAngle = rand() % 360;
+ float fireVelocity = 400.0f;
+ bolt->setVelocity(Vector2D(fireVelocity * cos(shotAngle), fireVelocity * sin(shotAngle)));
+ }
+ damages /= 2;
+ SoundManager::getInstance().playSound(SOUND_ELECTRIC_CHARGE);
+ }
+ return;
+ }
}
}
- isDying = true;
+ onDying();
if (boltType == ShotTypeBomb)
explode();
else
{
for (int i=0; i<5; i++)
{
Vector2D vel(40.0f + rand() % 50);
generateParticule(vel);
}
}
}
void BoltEntity::generateParticule(Vector2D vel)
{
SpriteEntity* trace = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_BOLT), x, y, BOLT_WIDTH, BOLT_HEIGHT);
trace->setFading(true);
trace->setZ(y);
trace->setLifetime(0.5f);
trace->setScale(0.3f, 0.3f);
trace->setVelocity(vel);
trace->setViscosity(0.97f);
trace->setType(ENTITY_EFFECT);
trace->setFrame(frame);
}
bool BoltEntity::collideWithMap(int direction)
{
calculateBB();
int xTile0 = (boundingBox.left - offsetX) / tileWidth;
int xTilef = (boundingBox.left + boundingBox.width - offsetX) / tileWidth;
int yTile0 = (boundingBox.top - offsetY) / tileHeight;
int yTilef = (boundingBox.top + boundingBox.height - offsetY) / tileHeight;
if (boundingBox.top < 0) yTile0 = -1;
for (int xTile = xTile0; xTile <= xTilef; xTile++)
for (int yTile = yTile0; yTile <= yTilef; yTile++)
{
if (boltType != ShotTypeIllusion)
{
if (flying)
{
if ( dynamic_cast<DungeonMap*>(map)->isFlyable(xTile, yTile) == false ) return true;
}
else
{
if ( dynamic_cast<DungeonMap*>(map)->isShootable(xTile, yTile) == false ) return true;
}
}
}
return false;
}
void BoltEntity::onDying()
{
- isDying = true;
+ if (!isDying)
+ {
+ isDying = true;
+
+ // special attacks
+ if (boltType == ShotTypeIce && level == 2)
+ new EffectZoneEntity(x, y, true, 5.0f, EffectZoneTypeIce);
+
+ else if (boltType == ShotTypePoison && level == 2)
+ {
+ if (!game().existsEffectZone())
+ new EffectZoneEntity(x, y, true, 5.0f, EffectZoneTypePoison);
+ }
+ }
}
void BoltEntity::stuck()
{
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
if (boltType == ShotTypeBomb)
explode();
else
{
for (int i=0; i<5; i++)
{
Vector2D vel(100.0f + rand() % 150);
generateParticule(vel);
}
}
-
onDying();
}
+void BoltEntity::split(int direction)
+{
+ switch (direction)
+ {
+ case 4: x += 8; break;
+ case 6: x -= 8; break;
+ case 2: y -= 8; break;
+ case 8: y += 8; break;
+ }
+ float currentVelocity = velocity.norm();
+ lifetime *= 1.25f;
+ BoltEntity* bolt2 = new BoltEntity(x, y, lifetime, ShotTypeStone, 1);
+ level = 1;
+ damages *= 0.75f;
+ bolt2->setDamages(damages);
+ bolt2->setX(x);
+ bolt2->setY(y);
+ bolt2->setAge(age);
+ if (direction == 2 || direction == 8)
+ {
+ velocity.x = -currentVelocity;
+ velocity.y = 0.0f;
+
+ bolt2->setVelocity(Vector2D(currentVelocity, 0.0f));
+ }
+ else
+ {
+ velocity.y = -currentVelocity;
+ velocity.x = 0.0f;
+
+ bolt2->setVelocity(Vector2D(0.0f, currentVelocity));
+ }
+ SoundManager::getInstance().playSound(SOUND_STONE_HIT);
+}
+
+void BoltEntity::collideWall()
+{
+ if (boltType == ShotTypeFire && level == 2)
+ {
+ if (!game().existsEffectZone())
+ new EffectZoneEntity(x, y, true, 5.0f, EffectZoneTypeFire, game().getPlayer()->getDps());
+ }
+}
+
void BoltEntity::collideMapRight()
{
if (boltType == ShotTypeLightning)
{
velocity.x = -velocity.x;
}
else if (boltType == ShotTypeBomb)
+ {
explode();
+ }
+ else if (boltType == ShotTypeStone && level == 2)
+ {
+ split(6);
+ }
else
{
velocity.x = 0.0f;
- isDying = true;
+ onDying();
+ collideWall();
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
for (int i=0; i<5; i++)
{
Vector2D vel(100.0f + rand() % 150);
if (vel.x > 0.0f) vel.x = - vel.x;
generateParticule(vel);
}
}
}
void BoltEntity::collideMapLeft()
{
if (boltType == ShotTypeLightning)
{
velocity.x = -velocity.x;
}
else if (boltType == ShotTypeBomb)
+ {
explode();
+ }
+ else if (boltType == ShotTypeStone && level == 2)
+ {
+ split(4);
+ }
else
{
velocity.x = 0.0f;
- isDying = true;
+ onDying();
+ collideWall();
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
for (int i=0; i<5; i++)
{
Vector2D vel(100.0f + rand() % 150);
if (vel.x < 0.0f) vel.x = - vel.x;
generateParticule(vel);
}
}
}
void BoltEntity::collideMapTop()
{
if (boltType == ShotTypeLightning)
{
velocity.y = -velocity.y;
}
else if (boltType == ShotTypeBomb)
+ {
explode();
+ }
+ else if (boltType == ShotTypeStone && level == 2)
+ {
+ split(8);
+ }
else
{
velocity.y = 0.0f;
- isDying = true;
+ onDying();
+ collideWall();
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
for (int i=0; i<5; i++)
{
Vector2D vel(100.0f + rand() % 150);
if (vel.y < 0.0f) vel.y = - vel.y;
generateParticule(vel);
}
}
}
void BoltEntity::collideMapBottom()
{
if (boltType == ShotTypeLightning)
{
velocity.y = -velocity.y;
}
else if (boltType == ShotTypeBomb)
+ {
explode();
+ }
+ else if (boltType == ShotTypeStone && level == 2)
+ {
+ split(2);
+ }
else
{
velocity.y = 0.0f;
- isDying = true;
+ onDying();
+ collideWall();
SoundManager::getInstance().playSound(SOUND_WALL_IMPACT);
for (int i=0; i<5; i++)
{
Vector2D vel(100.0f + rand() % 150);
if (vel.y > 0.0f) vel.y = - vel.y;
generateParticule(vel);
}
}
}
void BoltEntity::explode()
{
- isDying = true;
+ onDying();
new ExplosionEntity(x, y, ExplosionTypeStandard, 12, enemyType, true);
game().makeShake(0.5f);
SoundManager::getInstance().playSound(SOUND_BOOM_00);
game().addCorpse(x, y, FRAME_CORPSE_SLIME_VIOLET);
}
diff --git a/src/BoltEntity.h b/src/BoltEntity.h
index 204b2ee..22f9c13 100644
--- a/src/BoltEntity.h
+++ b/src/BoltEntity.h
@@ -1,71 +1,73 @@
#ifndef BOLTENTITY_H
#define BOLTENTITY_H
#include "sfml_game/CollidingSpriteEntity.h"
#include "ParticleGenerator.h"
#include "Constants.h"
#include "BaseCreatureEntity.h"
/*! \class BoltEntity
* \brief bolt thrown by the player
*
* BoltEntity are the missile weapons thrown by the player.
* The can collide with an enemy (to hurt him) or with the walls.
*/
class BoltEntity : public CollidingSpriteEntity
{
public:
BoltEntity(float x, float y, float boltLifeTime, enumShotType boltType, int level);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
void collide();
void generateParticule(Vector2D vel);
int getDamages();
unsigned int getLevel();
void setDamages(int damages);
void loseDamages(int damages);
enumShotType getBoltType();
bool isFlying();
void setFlying(bool flying);
bool isCritical();
void setCritical(bool setCritical);
void setGoThrough(bool goThrough);
bool getGoThrough();
enemyTypeEnum getEnemyType();
void setFromPlayer(bool fromPlayer);
bool isFromPlayer();
protected:
virtual void calculateBB();
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual bool collideWithMap(int direction);
+ virtual void collideWall();
virtual void onDying();
virtual void stuck();
void explode();
int damages;
float renderScale;
enumShotType boltType;
enemyTypeEnum enemyType;
ParticleGenerator particleGenerator;
private:
+ void split(int direction);
bool testWallsCollision;
bool flying;
bool critical;
bool goThrough;
unsigned int level;
int hitNumber;
bool fromPlayer;
};
#endif // BOLTENTITY_H
diff --git a/src/BubbleEntity.cpp b/src/BubbleEntity.cpp
index d192066..7cdf8c5 100644
--- a/src/BubbleEntity.cpp
+++ b/src/BubbleEntity.cpp
@@ -1,206 +1,206 @@
#include "BubbleEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
BubbleEntity::BubbleEntity(float x, float y, EnumBubbleType bubbleType, int bubbleSize)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_BUBBLE), x, y)
{
this->bubbleSize = bubbleSize;
this->bubbleType = bubbleType;
imagesProLine = 2;
frame = 0;
enemyType = EnemyTypeBubble;
if (bubbleSize == 0)
{
hp = 20;
creatureSpeed = 150.0f;
}
else if (bubbleSize == 1)
{
hp = 18;
creatureSpeed = 180.0f;
sprite.setScale(0.7, 0.7);
age = 0.0f;
}
else if (bubbleSize == 2)
{
hp = 16;
creatureSpeed = 210.0f;
sprite.setScale(0.5, 0.5);
age = 0.0f;
}
else if (bubbleSize == 3)
{
hp = 12;
creatureSpeed = 225.0f;
sprite.setScale(0.35, 0.35);
age = 0.0f;
}
else if (bubbleSize == 4)
{
hp = 5;
creatureSpeed = 240.0f;
sprite.setScale(0.2, 0.2);
age = 0.0f;
}
velocity = Vector2D(creatureSpeed);
computeFacingDirection();
meleeDamages = 5;
bloodColor = BloodNone;
shadowFrame = 1;
if (bubbleType == BubbleIce)
{
frame = 2;
shadowFrame = 3;
meleeType = ShotTypeIce;
resistance[ResistanceFrozen] = ResistanceImmune;
resistance[ResistanceIce] = ResistanceVeryHigh;
resistance[ResistanceFire] = ResistanceVeryLow;
}
else
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_EYE));
imagesProLine = 12;
shadowFrame = 23;
spin = 20 + rand() % 20;
}
width = 128;
height = 128;
sprite.setOrigin(64, 64);
canExplode = false;
}
int BubbleEntity::getBubbleSize()
{
return bubbleSize;
}
void BubbleEntity::calculateBB()
{
boundingBox.left = (int)x - 50 * sprite.getScale().x;
boundingBox.width = 100 * sprite.getScale().x;
boundingBox.top = (int)y - 50 * sprite.getScale().x;
boundingBox.height = 100 * sprite.getScale().x;
}
void BubbleEntity::animate(float delay)
{
if (bubbleType != BubbleIce)
{
if (age > 0)
frame = ((int)(age * (28.0f + 5.0f * (float)bubbleSize))) % 18;
else
frame = 0;
}
EnemyEntity::animate(delay);
}
void BubbleEntity::render(sf::RenderTarget* app)
{
if (bubbleType != BubbleIce)
{
// shadow
sprite.setPosition(x, y + 5);
sprite.setTextureRect(sf::IntRect(11 * width, 1 * height, width, height));
app->draw(sprite);
// eye
sprite.setRotation(age * spin);
sprite.setPosition(x, y);
int nx = frame % imagesProLine;
int ny = frame / imagesProLine;
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
// lightning
sprite.setRotation(0);
sprite.setTextureRect(sf::IntRect(6 * width, height, width, height));
app->draw(sprite);
nx = 7 + (int)(age * 5) % 4;
sprite.setTextureRect(sf::IntRect(nx * width, height, width, height));
app->draw(sprite);
}
else
EnemyEntity::render(app);
}
void BubbleEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void BubbleEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void BubbleEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void BubbleEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void BubbleEntity::collideWithEnemy(EnemyEntity* entity)
{
BubbleEntity* bubbleEntity = dynamic_cast<BubbleEntity*>(entity);
if (bubbleEntity == NULL)
{
if (entity->getMovingStyle() == movWalking)
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), creatureSpeed ));
}
else
{
if (bubbleEntity->getBubbleSize() <= bubbleSize)
setVelocity(Vector2D(bubbleEntity->getX(), bubbleEntity->getY()).vectorTo(Vector2D(x, y), creatureSpeed ));
}
}
void BubbleEntity::dying()
{
isDying = true;
if (bubbleSize == 0) game().addKilledEnemy(enemyType, hurtingType);
if (bubbleSize < 4)
{
int nbBubbles = 2;
if (bubbleType == BubbleTriple) nbBubbles = 3;
for (int i = 0; i < nbBubbles; i++)
{
BubbleEntity* b = new BubbleEntity(x - 5 + rand() % 10, y - 5 + rand() % 10, bubbleType, bubbleSize + 1);
b->setSpecialState(SpecialStateIce,
true,
specialState[SpecialStateIce].timer,
specialState[SpecialStateIce].param1,
specialState[SpecialStateIce].param2);
}
}
for (int i = 0; i < 5 - bubbleSize; i++)
game().generateBlood(x, y, bubbleType == BubbleIce ? BloodBubbleIce : BloodBubble);
if (bubbleType == BubbleIce)
SoundManager::getInstance().playSound(SOUND_ICE_BLOCK);
else
SoundManager::getInstance().playSound(SOUND_BUBBLE_00 + rand() % 2);
}
diff --git a/src/ButcherEntity.cpp b/src/ButcherEntity.cpp
index 7e58881..3ae8e38 100644
--- a/src/ButcherEntity.cpp
+++ b/src/ButcherEntity.cpp
@@ -1,168 +1,168 @@
#include "ButcherEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "SausageEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
ButcherEntity::ButcherEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_BUTCHER), x, y)
{
width = 80;
height = 160;
sprite.setOrigin(40, 115);
creatureSpeed = BUTCHER_VELOCITY;
velocity = Vector2D(creatureSpeed);
computeFacingDirection();
hp = BUTCHER_HP;
hpMax = BUTCHER_HP;
hpDisplay = BUTCHER_HP;
meleeDamages = BUTCHER_DAMAGES;
sausages = 0;
type = ENTITY_ENEMY_BOSS;
bloodColor = BloodRed;
shadowFrame = 4;
dyingFrame = 3;
deathFrame = FRAME_CORPSE_BUTCHER;
agonizingSound = SOUND_BUTCHER_DIE;
hurtingSound = SOUND_BUTCHER_HURT;
enemyType = EnemyTypeButcher;
timer = (rand() % 50) / 10.0f;
age = -1.5f;
frame = 1;
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceHigh;
+ resistance[ResistanceRepulsion] = ResistanceHigh;
canExplode = false;
}
void ButcherEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
sprite.setColor(sf::Color(255,255,255,255));
timer = timer - delay;
if (timer <= 0.0f)
{
creatureSpeed = BUTCHER_VELOCITY + (hpMax - hp) * 0.8f;
timer = (rand() % 50) / 10.0f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
if (rand()%2 == 0)
SoundManager::getInstance().playSound(SOUND_BUTCHER_00);
else
SoundManager::getInstance().playSound(SOUND_BUTCHER_01);
}
frame = ((int)(age * creatureSpeed / 25)) % 4;
if (frame == 3) frame = 1;
if (velocity.x > 1.0f) isMirroring = true;
else if (velocity.x < -1.0f) isMirroring = false;
}
EnemyEntity::animate(delay);
z = y + 30;
}
void ButcherEntity::calculateBB()
{
boundingBox.left = (int)x - 22;
boundingBox.width = 44;
boundingBox.top = (int)y - 18;
boundingBox.height = 48;
}
void ButcherEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void ButcherEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void ButcherEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void ButcherEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void ButcherEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking && entity->getEnemyType() != EnemyTypeSausage_invocated)
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 100.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
}
}
void ButcherEntity::drop()
{
dropItem(ItemScrollRevelation);
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
EnemyEntity* entity = dynamic_cast<EnemyEntity*>(e);
if (entity != NULL)
{
if (entity->getEnemyType()== EnemyTypeSausage_invocated)
{
entity->hurt(getHurtParams(entity->getHp(), ShotTypeStandard, 0, false, SourceTypeMelee, enemyType, false));
}
}
}
}
void ButcherEntity::render(sf::RenderTarget* app)
{
EnemyEntity::render(app);
renderLifeBar(app, tools::getLabel("enemy_butcher"));
}
int ButcherEntity::hurt(StructHurt hurtParam)
{
creatureSpeed = BUTCHER_VELOCITY + hpMax - hp;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
int result = EnemyEntity::hurt(hurtParam);
int totalDamages = hpMax - hp;
if (hp > 0 && totalDamages / 8 > sausages)
{
sausages++;
new SausageEntity(x, y, true);
}
return result;
}
bool ButcherEntity::isAttacking()
{
return true;
}
diff --git a/src/CauldronEntity.cpp b/src/CauldronEntity.cpp
index 88e5d1e..98fa60e 100644
--- a/src/CauldronEntity.cpp
+++ b/src/CauldronEntity.cpp
@@ -1,248 +1,250 @@
#include "CauldronEntity.h"
#include "SlimeEntity.h"
#include "ExplosionEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
CauldronEntity::CauldronEntity(float x, float y, cauldronTypeEnum cauldronType)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_CAULDRON), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = CAULDRON_HP;
hpMax = hp;
meleeDamages = 0;
this->cauldronType = cauldronType;
bloodColor = BloodNone;
invokeDelay = 2.5f;
bubbleDelay = 0.0f;
shadowFrame = 2;
sprite.setOrigin(32, 28);
imagesProLine = 2;
deathFrame = FRAME_CORPSE_CAULDRON;
dyingSound = SOUND_CAULDRON_DIE;
if (cauldronType == CauldronTypeElemental)
{
enemyType = EnemyTypeCauldronElemental;
colorChangeDelay = 4.0f + rand()% 40 * 0.1f;
colorState = rand() % 3;
}
else
{
enemyType = EnemyTypeCauldron;
}
enemyType = cauldronType == CauldronTypeStandard ? EnemyTypeCauldron : EnemyTypeCauldronElemental;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
}
void CauldronEntity::animate(float delay)
{
if (isAgonising)
{
agonizingDelay -= delay;
if (agonizingDelay <= 0.0f)
{
isDying = true;
game().addCorpse(x, y, deathFrame);
}
}
else
{
SoundManager::getInstance().playSound(SOUND_CAULDRON, false);
invokeDelay -= delay;
if (invokeDelay < 0.0f)
{
if (cauldronType == CauldronTypeElemental)
{
if (game().getEnemyCount() < 9)
{
slimeTypeEnum slimeType = SlimeTypeStandard;
switch (colorState)
{
case 0: slimeType = SlimeTypeBlue; break;
case 1: slimeType = SlimeTypeRed; break;
case 2: slimeType = SlimeTypeStandard; break;
}
- new SlimeEntity(x, y, slimeType, true);
+ SlimeEntity* slime = new SlimeEntity(x, y, slimeType, true);
+ slime->disableCollidingTemporary();
invokeDelay = 3.75f + (float)(rand() % 3000) / 1000.0f;
}
}
else
{
- new SlimeEntity(x, y, SlimeTypeViolet, true);
+ SlimeEntity* slime = new SlimeEntity(x, y, SlimeTypeViolet, true);
+ slime->disableCollidingTemporary();
invokeDelay = 1.5f + (float)(rand() % 2500) / 1000.0f;
}
}
if (cauldronType == CauldronTypeElemental)
{
colorChangeDelay -= delay;
if (colorChangeDelay < 0.0f)
{
colorChangeDelay = 4.0f + rand()% 50 * 0.1f;
if (rand() % 2 == 0)
{
colorState++;
if (colorState > 2) colorState = 0;
}
else
{
colorState--;
if (colorState < 0) colorState = 2;
}
}
}
bubbleDelay -= delay;
if (bubbleDelay < 0.0f)
{
bubbleDelay = 0.3f;
int bubbleFrame = 32;
if (cauldronType == CauldronTypeElemental)
{
switch (colorState)
{
case 0: bubbleFrame = 33; break;
case 1: bubbleFrame = 34; break;
case 2: bubbleFrame = 35; break;
}
}
for (int i=0; i < 2; i++)
{
float xBub = x - 16 + rand() % 32;
SpriteEntity* bubble = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_CAULDRON), xBub, y - 20, 8, 8);
bubble->setZ(z);
bubble->setFrame(bubbleFrame);
bubble->setType(ENTITY_EFFECT);
bubble->setWeight(-20 - rand() % 40);
bubble->setLifetime(2.0f);
float bloodScale = 0.3f + (rand() % 20) * 0.1f;
bubble->setScale(bloodScale, bloodScale);
}
}
frame = hp > hpMax / 2 ? 0 : 1;
if (cauldronType == CauldronTypeElemental)
{
switch (colorState)
{
case 0: frame += 2; break;
case 1: frame += 4; break;
case 2: frame += 6; break;
}
}
EnemyEntity::animate(delay);
}
}
void CauldronEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead())
{
inflictsRecoilTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
collideWithBolt(boltEntity);
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
void CauldronEntity::calculateBB()
{
boundingBox.left = (int)x - 25;
boundingBox.width = 50;
boundingBox.top = (int)y - 10;
boundingBox.height = 42;
}
void CauldronEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void CauldronEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void CauldronEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void CauldronEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void CauldronEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() != movFlying)
inflictsRecoilTo(entity);
}
void CauldronEntity::dying()
{
new ExplosionEntity(x, y, ExplosionTypeViolet, 0, EnemyTypeNone, true);
if (cauldronType == CauldronTypeElemental)
{
switch (colorState)
{
case 0: deathFrame = FRAME_CORPSE_CAULDRON_RED; break;
case 1: deathFrame = FRAME_CORPSE_CAULDRON_BLUE; break;
case 2: deathFrame = FRAME_CORPSE_CAULDRON_GREEN; break;
}
}
EnemyEntity::dying();
return;
}
void CauldronEntity::drop()
{
EnemyEntity::drop();
}
void CauldronEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 200.0f );
- targetEntity->giveRecoil(false, recoilVector, 0.1f);
+ targetEntity->giveRepulsion(false, recoilVector, 0.1f);
}
diff --git a/src/Constants.h b/src/Constants.h
index 96bffd0..cf6ed8d 100644
--- a/src/Constants.h
+++ b/src/Constants.h
@@ -1,727 +1,739 @@
/** This file is part of Witch Blast.
*
* Witch Blast is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Witch Blast is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONSTANTS_H_INCLUDED
#define CONSTANTS_H_INCLUDED
#include <string>
const std::string APP_NAME = "Witch Blast";
-const std::string APP_VERSION = "0.7.5";
+const std::string APP_VERSION = "0.7.6";
const std::string CONFIG_FILE = "config.dat";
const std::string SAVE_FILE = "game.sav";
const std::string SAVE_DATA_FILE = "data/data.sav";
const std::string HISCORES_FILE = "data/scores.dat";
-const std::string SAVE_VERSION = "SAVE_0.7";
-const std::string SCORE_VERSION = "V075";
+const std::string SAVE_VERSION = "SAVE_0.8";
+const std::string SCORE_VERSION = "V075_DEV";
const int NB_LANGUAGES = 5;
const std::string languageString[NB_LANGUAGES] = { "english", "french", "german", "spanish", "russian" };
const std::string languageState[NB_LANGUAGES] = { "", "", "", "", "" };
const unsigned int SCORES_MAX = 10;
const int LAST_LEVEL = 8;
// Client size
const int SCREEN_WIDTH = 970;
const int SCREEN_HEIGHT = 720;
// Tile set
const int TILE_WIDTH = 64;
const int TILE_HEIGHT = 64;
// Tile map offset
const int OFFSET_X = 5;
const int OFFSET_Y = 5;
const int MAP_WIDTH = 15;
const int MAP_HEIGHT = 9;
const int MENU_MAP_WIDTH = 16;
const int MENU_MAP_HEIGHT = 13;
const int GAME_WIDTH = MAP_WIDTH * TILE_WIDTH;
const int GAME_HEIGHT = MAP_HEIGHT * TILE_HEIGHT;
const int FLOOR_WIDTH = 13;
const int FLOOR_HEIGHT = 7;
const int ITEM_WIDTH = 32;
const int ITEM_HEIGHT = 32;
const int BOLT_WIDTH = 24;
const int BOLT_HEIGHT = 24;
const int BOLT_PRO_LINE = 10;
const int BB_LEFT = 22;
const int BB_RIGHT = 22;
const int BB_TOP = 4;
const int BB_BOTTOM = 31;
const float FADE_IN_DELAY = 1.0f;
const float FADE_OUT_DELAY = 1.0f;
const float EXPLOSION_DELAY = 2.0f;
const float DEATH_CERTIFICATE_DELAY = 3.5f;
const float ACHIEVEMENT_DELAY_MAX = 15.0f;
const float KeyRoomFXDelay = 2.0f;
enum enum_images {
IMAGE_PLAYER_0,
IMAGE_PLAYER_1,
IMAGE_PLAYER_2,
IMAGE_BOLT,
IMAGE_TILES,
IMAGE_RAT,
IMAGE_MINIMAP,
IMAGE_MAP_BACKGROUND,
IMAGE_ITEMS,
IMAGE_ITEMS_EQUIP,
IMAGE_ITEMS_PRES,
IMAGE_ITEMS_EQUIP_PRES,
IMAGE_CHEST,
IMAGE_BAT,
IMAGE_FLOWER,
IMAGE_SLIME,
IMAGE_IMP,
IMAGE_SPIDER_EGG,
IMAGE_SPIDER_WEB,
IMAGE_LITTLE_SPIDER,
IMAGE_BUBBLE,
IMAGE_EYE,
IMAGE_WITCH,
IMAGE_CAULDRON,
IMAGE_SNAKE,
IMAGE_PUMPKIN,
IMAGE_GHOST,
IMAGE_ZOMBIE,
IMAGE_BOGEY,
IMAGE_SAUSAGE,
IMAGE_BUTCHER,
IMAGE_GIANT_SLIME,
IMAGE_KING_RAT,
IMAGE_CYCLOP,
IMAGE_GIANT_SPIDER,
IMAGE_FRANCKY,
IMAGE_VAMPIRE,
IMAGE_VAMPIRE_BAT,
IMAGE_VAMPIRE_PART,
IMAGE_BLOOD,
IMAGE_CORPSES,
IMAGE_CORPSES_BIG,
IMAGE_STAR,
IMAGE_STAR_2,
IMAGE_HURT_IMPACT,
IMAGE_INTERFACE,
IMAGE_HUD_SHOTS,
IMAGE_EXPLOSION,
IMAGE_KEYS_QWER,
IMAGE_KEYS_AZER,
IMAGE_INTRO,
IMAGE_TITLE,
IMAGE_OVERLAY,
IMAGE_LIGHT_CONE,
IMAGE_DIVINITY,
IMAGE_PNJ,
IMAGE_FAIRY,
IMAGE_KEY_AREA,
IMAGE_UI_LIFE,
IMAGE_UI_MANA,
IMAGE_UI_SPELLS,
IMAGE_UI_MESSAGE,
IMAGE_UI_TOP_LAYER,
IMAGE_UI_ACHIEV,
IMAGE_FOG,
IMAGE_TITLE_ANIM,
IMAGE_SPLATTER,
IMAGE_WITCH_INTRO,
IMAGE_ITEM_DESCRIPTION,
IMAGE_DEATH_CERTIFICATE,
IMAGE_ACHIEVEMENTS,
IMAGE_BOSS_PICTURES,
IMAGE_PORTRAIT_PART,
IMAGE_RANDOM_DUNGEON,
IMAGE_DUNGEON_OBJECTS,
IMAGE_TILES_SHADOW,
IMAGE_TILES_SHADOW_CORNER,
IMAGE_TILES_SHADOW_MEDIUM,
IMAGE_TILES_SHADOW_SMALL,
IMAGE_DOORS,
IMAGE_DESTROYABLE,
IMAGE_HALL_OF_FAME,
IMAGE_LIGHTNING,
IMAGE_WIN_SEAL,
IMAGE_WIN_SEAL_HOF,
IMAGE_BAG,
IMAGE_UI_PAUSE,
IMAGE_SCORE_FONT,
+ IMAGE_EFFECT_ZONE,
NB_IMAGES
};
enum sound_resources {
SOUND_NONE = -1,
SOUND_BLAST_STANDARD,
SOUND_BLAST_FLOWER,
SOUND_BLAST_LIGHTNING,
SOUND_BLAST_FIRE,
SOUND_BLAST_ICE,
SOUND_BLAST_ILLUSION,
SOUND_BLAST_POISON,
SOUND_BLAST_STONE,
SOUND_DOOR_CLOSING,
SOUND_DOOR_OPENING,
SOUND_CHEST_OPENING,
SOUND_IMPACT,
SOUND_BONUS,
SOUND_DRINK,
SOUND_EAT,
SOUND_PLAYER_HIT,
SOUND_PLAYER_DIE,
SOUND_ENNEMY_DYING,
SOUND_COIN_PICK_UP,
SOUND_PAY,
SOUND_WALL_IMPACT,
SOUND_BIG_WALL_IMPACT,
SOUND_KING_RAT_1,
SOUND_KING_RAT_2,
SOUND_KING_RAT_DIE,
SOUND_SLIME_JUMP,
SOUND_SLIME_IMAPCT,
SOUND_SLIME_IMAPCT_WEAK,
SOUND_SLIME_IMAPCT_BOSS,
SOUND_SLIME_SMASH,
SOUND_ICE_CHARGE,
SOUND_ELECTRIC_CHARGE,
SOUND_SHOT_SELECT,
SOUND_HEART,
SOUND_RAT_DYING,
SOUND_BAT_DYING,
SOUND_IMP_HURT,
SOUND_IMP_DYING,
SOUND_ROCK_IMPACT_LIGHT,
SOUND_ROCK_IMPACT_MEDIUM,
SOUND_ROCK_IMPACT_HEAVY,
SOUND_THROW,
SOUND_CYCLOP_00,
SOUND_CYCLOP_DIE,
SOUND_CYCLOPS_IMPACT,
SOUND_BUTCHER_00,
SOUND_BUTCHER_01,
SOUND_BUTCHER_HURT,
SOUND_BUTCHER_DIE,
SOUND_VIB,
SOUND_BOOM_00,
SOUND_CLANG_00,
SOUND_BUBBLE_00,
SOUND_BUBBLE_01,
SOUND_TRAP,
SOUND_EGG_SMASH_00,
SOUND_EGG_SMASH_01,
SOUND_SPIDER_WALKING,
SOUND_SPIDER_WEB,
SOUND_SPIDER_HURT,
SOUND_SPIDER_DIE,
SOUND_SPIDER_LITTLE_DIE,
SOUND_WITCH_00,
SOUND_WITCH_01,
SOUND_WITCH_02,
SOUND_WITCH_DIE_00,
SOUND_WITCH_DIE_01,
SOUND_INVOKE,
SOUND_CAULDRON,
SOUND_CAULDRON_DIE,
SOUND_SNAKE_DIE,
SOUND_PUMPKIN_00,
SOUND_PUMPKIN_01,
SOUND_PUMPKIN_DIE,
SOUND_CRITICAL,
SOUND_GONG,
SOUND_TELEPORT,
SOUND_SPELL_CHARGE,
SOUND_FIREBALL,
SOUND_MESSAGE,
SOUND_EARTHQUAKE,
SOUND_SPELL_FREEZE,
SOUND_SPELL_SHIELD,
SOUND_HEAVY_STEP_00,
SOUND_HEAVY_STEP_01,
SOUND_NIGHT,
SOUND_GRUMBLE,
SOUND_ZOMBIE_00,
SOUND_ZOMBIE_01,
SOUND_ZOMBIE_ATTACKING,
SOUND_ZOMBIE_DYING,
SOUND_GHOST,
SOUND_GHOST_DYING,
SOUND_ELECTRICITY,
SOUND_ELECTRIC_BLAST,
SOUND_FRANCKY_00,
SOUND_FRANCKY_01,
SOUND_FRANCKY_02,
SOUND_FRANCKY_DYING,
SOUND_OM,
SOUND_GLASS,
SOUND_HICCUP,
SOUND_SPLATCH,
SOUND_INTRO_WITCH,
SOUND_FORCE_FIELD,
SOUND_DOOR_OPENING_BOSS,
SOUND_ACHIEVEMENT,
SOUND_VAMPIRE_FLYING,
SOUND_VAMPIRE_FLAP,
SOUND_VAMPIRE_SONIC_RAY,
SOUND_VAMPIRE_LAUGHING,
SOUND_VAMPIRE_TRANSFORM_BOLT,
SOUND_VAMPIRE_TRANSFORM_BAT,
SOUND_VAMPIRE_HYPNOSIS,
SOUND_VAMPIRE_CRY,
SOUND_VAMPIRE_DYING,
SOUND_ICE_BLOCK,
SOUND_BOGEYMAN_DIE,
SOUND_BOGEYMAN_ATTACK,
SOUND_BOGEYMAN_VORTEX_00,
SOUND_BOGEYMAN_VORTEX_01,
SOUND_BARREL_HIT,
SOUND_BARREL_SMASH,
SOUND_SECRET,
SOUND_SCROLL,
SOUND_CLOCK,
SOUND_BOTTLE,
SOUND_THUNDER,
- SOUND_SCORE_BONUS
+ SOUND_SCORE_BONUS,
+ SOUND_STONE_HIT,
};
enum corpses_ressources{
FRAME_CORPSE_RAT,
FRAME_CORPSE_BAT,
FRAME_CORPSE_FLOWER,
FRAME_CORPSE_GREEN_RAT,
FRAME_CORPSE_SLIME,
FRAME_CORPSE_BLACK_RAT,
FRAME_CORPSE_IMP_RED,
FRAME_CORPSE_IMP_BLUE,
FRAME_CORPSE_SLIME_RED,
FRAME_CORPSE_SLIME_BLUE,
FRAME_CORPSE_SPIDER_EGG,
FRAME_CORPSE_LITTLE_SPIDER,
FRAME_CORPSE_SPIDER_WEB,
FRAME_CORPSE_RAT_HELMET,
FRAME_CORPSE_BLACK_RAT_HELMET,
FRAME_CORPSE_SLIME_VIOLET,
FRAME_CORPSE_WITCH,
FRAME_CORPSE_WITCH_RED,
FRAME_CORPSE_CAULDRON,
FRAME_CORPSE_SNAKE,
FRAME_CORPSE_SNAKE_BLOOD,
FRAME_CORPSE_PUMPKIN,
FRAME_CORPSE_FLOWER_ICE,
FRAME_CORPSE_GHOST,
FRAME_CORPSE_ZOMBIE,
FRAME_CORPSE_ZOMBIE_DARK,
FRAME_CORPSE_FRANCKY_TORSO,
FRAME_CORPSE_FRANCKY_HEAD,
FRAME_CORPSE_FRANCKY_HAND,
FRAME_CORPSE_FRANCKY_FOOT,
FRAME_CORPSE_FLOWER_FIRE,
FRAME_CORPSE_VAMPIRE,
FRAME_CORPSE_BAT_SKELETON,
FRAME_CORPSE_CAULDRON_BLUE,
FRAME_CORPSE_CAULDRON_RED,
FRAME_CORPSE_CAULDRON_GREEN,
FRAME_CORPSE_LITTLE_SPIDER_TARANTULA,
FRAME_CORPSE_KING_RAT,
FRAME_CORPSE_GIANT_SLIME,
FRAME_CORPSE_CYCLOP,
FRAME_CORPSE_BUTCHER,
FRAME_CORPSE_GIANT_SPIDER,
FRAME_CORPSE_GIANT_SLIME_RED,
FRAME_CORPSE_GIANT_SLIME_BLUE,
};
// chests
enum chest_type_enum {
ChestBasic,
ChestExit,
ChestChallenge,
ChestFairy,
};
const float CHEST_APPEAR_DELAY = 1.5f;
// Artefact Info
const float ARTEFACT_RECT_WIDTH = 650.0f;
const float ARTEFACT_RECT_HEIGHT = 100.0f;
const float ARTEFACT_POS_Y = 450.0f;
const float ARTEFACT_BORDER = 3.0f;
const float ARTEFACT_ZOOM_TIME = 0.5f;
// shot types
enum enumShotType {
ShotTypeStandard,
ShotTypeIce,
ShotTypeIllusion,
ShotTypeStone,
ShotTypeLightning,
ShotTypeFire,
ShotTypePoison,
ShotTypeBomb,
ShotTypeCold,
ShotTypeDeterministic
};
// special shots effects
const int MAX_SHOT_LEVEL = 3;
const float STATUS_FROZEN_DELAY[MAX_SHOT_LEVEL] // how long the freeze occurs
= { 4.0f, 5.0f, 6.0f };
const float STATUS_FROZEN_BOLT_DELAY[MAX_SHOT_LEVEL] // reload time
- = { 3.0f, 2.6f, 2.0f };
+ = { 3.0f, 2.8f, 2.8f };
const float STATUS_FROZEN_MULT[MAX_SHOT_LEVEL] // speed multiplier (= 3 times slower)
= { 0.38f, 0.33f, 0.28f };
const float STONE_DECOIL_DELAY[MAX_SHOT_LEVEL] // how long the stun occurs
= { 0.15f, 0.175f, 0.2f };
const float STONE_DECOIL_VELOCITY[MAX_SHOT_LEVEL] // Decoil velocity
= { 270.0f, 320.0f, 450.0f };
const float ILLUSION_DAMAGE_DECREASE[MAX_SHOT_LEVEL] // Illusion damage malus
= { 0.8f, 0.9f, 1.0f };
const float LIGHTNING_VISCOSITY_INCREASE[MAX_SHOT_LEVEL] // Air resistance
= { 0.01, 0.015f, 0.02f };
const float FIRE_DAMAGE_INCREASE[MAX_SHOT_LEVEL] // Fire damage's bonus
= { 1.15f, 1.3f, 1.45f };
const float POISON_TIMER[MAX_SHOT_LEVEL]
= { 10.5f, 12.5f, 12.5f };
const float POISON_DAMAGE[MAX_SHOT_LEVEL]
= { 1, 1, 1 };
const float POISON_DELAY[MAX_SHOT_LEVEL]
= { 2.0f, 1.5f, 1.0f };
// divinity
enum enumDivinityType {
DivinityHealer,
DivinityFighter,
DivinityIce,
DivinityStone,
DivinityAir,
+ DivinityIllusion,
+ DivinityFire,
+ DivinityDeath,
NB_DIVINITY
};
const std::string divinityLabel[NB_DIVINITY]
{
"divinity_healer",
"divinity_fighter",
"divinity_ice",
"divinity_stone",
"divinity_air",
+ "divinity_illusion",
+ "divinity_fire",
+ "divinity_death",
};
const int MAX_DIVINITY_LEVEL = 5;
const int DIVINITY_LEVEL_TRESHOLD[MAX_DIVINITY_LEVEL] =
{ 100, 300, 500, 900, 1400};
// entity type
const int ENTITY_PLAYER = 1;
const int ENTITY_FAMILIAR = 2;
const int ENTITY_DOOR = 3;
const int ENTITY_ARTIFACT_DESCRIPTION = 9;
const int ENTITY_BLOOD = 11;
const int ENTITY_CORPSE = 12;
const int ENTITY_EFFECT = 13;
const int ENTITY_FLYING_TEXT = 14;
const int ENTITY_BOLT = 15;
const int ENTITY_ENEMY_BOLT = 16;
const int ENTITY_PNJ = 17;
const int ENTITY_CHEST = 18;
const int ENTITY_ITEM = 19;
const int ENTITY_EXPLOSION = 20;
const int ENTITY_FAMILIAR_LOCAL = 21;
+const int ENTITY_EFFECT_ZONE = 22;
const int ENTITY_ENEMY = 31;
const int ENTITY_ENEMY_INVOCATED = 32;
const int ENTITY_ENEMY_BOSS = 33;
const int ENTITY_ENEMY_NC = 34;
const int ENTITY_ENEMY_MAX = ENTITY_ENEMY_NC;
const int ENTITY_ENEMY_MAX_COUNT = ENTITY_ENEMY_BOSS;
const float DOOR_OPEN_TIME = 1.0f;
const float DOOR_CLOSE_TIME = 1.0f;
// Player game play
const float INITIAL_PLAYER_SPEED = 220.0f;
const int INITIAL_PLAYER_HP = 20;
const float INITIAL_PLAYER_FIRE_DELAY = 0.7f;
const float ACQUIRE_DELAY = 2.3f;
const float UNLOCK_DELAY = 1.2f;
const float WORSHIP_DELAY = 2.0f;
const float INVINCIBLE_DELAY = 1.5f;
const float HICCUP_DELAY = 5.0f;
const float INITIAL_BOLT_LIFE = 0.45f;
const int INITIAL_BOLT_DAMAGES = 8;
const float INITIAL_BOLT_VELOCITY = 700.0f;
const float INITIAL_BOLT_VISCOSITY = 0.98f;
const float FAIRY_SPEED = 220.0f;
const float FAIRY_FIRE_DELAY = 0.8f;
const float ICE_FAIRY_FIRE_DELAY = 1.3f;
const float TARGET_FAIRY_FIRE_DELAY = 1.0f;
const float FAIRY_BOLT_LIFE = 0.4f;
const int FAIRY_BOLT_DAMAGES = 8;
const int FAIRY_FIRE_DAMAGES = 12;
const float FAIRY_BOLT_VELOCITY = 700.0f;
// Rat
const float RAT_SPEED = 195.0f;
const float RAT_SPEED_HELMET = 140.0f;
const int RAT_HP = 16;
const int RAT_HP_HELMET = 24;
const int RAT_DAMAGES = 5;
const int RAT_BB_LEFT = 14;
const int RAT_BB_WIDTH_DIFF = 28;
const int RAT_BB_TOP = 22;
const int RAT_BB_HEIGHT_DIFF = 22;
// Green Rat
const float GREEN_RAT_SPEED = 180.0f;
const int GREEN_RAT_HP = 16;
const int GREEN_RAT_DAMAGES = 5;
const float GREEN_RAT_FADE = 1.0f;
// Black Rat
const float BLACK_RAT_SPEED = 195.0f;
const float BLACK_RAT_SPEED_HELMET = 125.0f;
const int BLACK_RAT_HP = 16;
const int BLACK_RAT_HP_HELMET = 24;
const int BLACK_RAT_DAMAGES = 5;
// Bat
const float BAT_SPEED = 270.0f;
const int BAT_HP = 5;
const int BAT_DAMAGES = 5;
// Zombie
const float ZOMBIE_SPEED = 80.0f;
const int ZOMBIE_HP = 80;
const int ZOMBIE_DAMAGE = 10;
// Evl Flower
const int EVIL_FLOWER_HP = 16;
const int EVIL_FLOWER_MELEE_DAMAGES = 5;
const int EVIL_FLOWER_MISSILE_DAMAGES = 6;
const int EVIL_FLOWER_FIRE_MISSILE_DAMAGES = 8;
const int EVIL_FLOWER_BB_LEFT = 14;
const int EVIL_FLOWER_BB_WIDTH_DIFF = 28;
const int EVIL_FLOWER_BB_TOP = 22;
const int EVIL_FLOWER_BB_HEIGHT_DIFF = 22;
const float EVIL_FLOWER_FIRE_DELAY = 2.2f;
const float EVIL_FLOWER_FIRE_VELOCITY = 230.0f;
// Snake
const float SNAKE_SPEED = 150.0f;
const int SNAKE_HP = 16;
const int SNAKE_DAMAGE = 5;
const float SNAKE_BLOOD_SPEED = 170.0f;
const int SNAKE_BLOOD_HP = 32;
const int SNAKE_BLOOD_DAMAGE = 8;
// Slime
const int SLIME_HP = 16;
const int SLIME_DAMAGES = 5;
const int SLIME_BB_LEFT = 13;
const int SLIME_BB_WIDTH_DIFF = 26;
const int SLIME_BB_TOP = 38;
const int SLIME_BB_HEIGHT_DIFF = 40;
const float SLIME_FIRE_VELOCITY = 240.0f;
// Imp
const float IMP_SPEED = 190.0f;
const int IMP_HP = 20;
const int IMP_MELEE_DAMAGES = 5;
const int IMP_MISSILE_DAMAGES = 8;
const float IMP_FIRE_VELOCITY = 260.0f;
// Witch
const int WITCH_HP = 80;
const int WITCH_DAMAGE = 5;
const int WITCH_VELOCITY = 140.0f;
// Cauldron
const int CAULDRON_HP = 150;
// Ghost
const float GHOST_SPEED = 140.0f;
const int GHOST_HP = 40;
const int GHOST_DAMAGE = 8;
// Butcher
const int BUTCHER_HP = 120;
const int BUTCHER_DAMAGES = 8;
const int BUTCHER_VELOCITY = 100;
// Giant Slime
const int GIANT_SLIME_HP = 250;
const int GIANT_SLIME_DAMAGES = 8;
const int GIANT_SLIME_MISSILE_DAMAGES = 6;
const float GIANT_SLIME_MISSILE_DELAY = 0.33f;
const float GIANT_SLIME_FIRE_VELOCITY = 200.0f;
const int GIANT_SLIME_BB_LEFT = 26;
const int GIANT_SLIME_BB_WIDTH_DIFF = 52;
const int GIANT_SLIME_BB_TOP = 64;
const int GIANT_SLIME_BB_HEIGHT_DIFF = 12;
const int GIANT_SLIME_SPEED = 80.0f;
const float GIANT_SLIME_ULTRA_JUMP_VELOCITY = 1700.0f;
const float GIANT_SLIME_ULTRA_FALL_VELOCITY = -2000.0f;
const float GIANT_SLIME_ULTRA_JUMP_TIMER = 2.3f;
// KingRat
const float KING_RAT_SPEED = 210.0f;
const float KING_RAT_RUNNING_SPEED = 750.0f;
const float KING_RAT_BERSERK_SPEED = 300.0f;
const float KING_RAT_RUNNING_RECOIL = 750.0f;
const int KING_RAT_HP = 700;
const int KING_RAT_DAMAGES = 8;
// Cyclops
const float CYCLOP_SPEED[4] = { 140, 150, 160, 170};
const int CYCLOP_NUMBER_ROCKS[4] = { 5, 7, 9, 12};
const float CYCLOP_FIRE_DELAY[4] = { 0.3f, 0.26f, 0.23f, 0.2f};
const int CYCLOP_HP = 880;
const int CYCLOP_DAMAGES = 8;
// Giant Spider
const float GIANT_SPIDER_SPEED[4] = { 240, 245, 250, 255 };
const int GIANT_SPIDER_NUMBER_EGGS[4] = { 12, 14, 16, 18};
const float GIANT_SPIDER_FIRE_DELAY[4] = { 0.82f, 0.72f, 0.62f, 0.52f};
const int GIANT_SPIDER_HP = 800;
const int GIANT_SPIDER_DAMAGE = 8;
// EFFECTS
const float HURTING_DELAY = 0.4f;
const float HEART_BEAT_DELAY = 1.2f;
// messages
const int NB_MSG_LINES = 3;
enum EnumMessages
{
MsgInfoLevel1,
MsgInfoLevel2,
MsgInfoLevel3,
MsgInfoLevel4,
MsgInfoLevel5,
MsgInfoLevel6,
MsgInfoLevel7,
MsgInfoRatsBats,
MsgInfoSnakes,
MsgInfoWitches,
MsgInfoGold,
MsgInfoFamiliar,
MsgInfoButcher,
MsgInfoGiantSlime,
MsgInfoCyclops,
MsgInfoWererat,
MsgInfoGiantSpiderBefore,
MsgInfoGiantSpiderAfter,
MsgInfoFranky,
MsgInfoVampire,
MsgInfoDivHealer,
MsgInfoDivFighter,
MsgInfoDivIce,
MsgInfoDivStone,
MsgInfoDivAir,
+ MsgInfoDivIllusion,
+ MsgInfoDivFire,
+ MsgInfoDivPoison,
MsgInfoDivIntervention,
MsgInfoDivGift,
MsgInfoDivLevel,
MsgInfoPotionId,
MsgTutoBasics,
MsgTutoTips,
MsgTutoItems,
MsgTutoShops,
MsgTutoBossDoor,
MsgTutoChallengeDoor,
MsgTutoTemple,
MsgTutoHeal,
MsgTutoShots,
MsgTutoSpell,
MsgTutoAchievements,
MsgTutoConsumables,
MsgTutoPotions,
NB_MESSAGES
};
enum EnumMessagesType
{
MessageTypeInfo,
MessageTypeTutorial,
};
struct messageStruct
{
EnumMessagesType messageType;
EnumMessages type;
std::string message[NB_MSG_LINES];
float timer;
float timerMax;
};
enum EnumWorldEvents
{
// info
EventMeetRatsOrBats,
EventMeetSnakes,
EventMeetWitches,
EventGetCoin,
EventPietyMax,
Event4Hits,
// tuto
EventBeingHurted,
EventFindShop,
EventFindBossDoor,
EventFindChallengeDoor,
EventFindTemple,
EventGetItem,
EventGetSpecialShot,
EventGetFamiliar,
EventGetSpell,
EventAchievement,
EventConsumable,
EventPotion,
NB_EVENTS
};
enum EnumInteractionType
{
InteractionTypeTemple,
InteractionTypeMerchandise,
};
#endif // CONSTANTS_H_INCLUDED
diff --git a/src/CyclopsEntity.cpp b/src/CyclopsEntity.cpp
index a3b2910..02aaf53 100644
--- a/src/CyclopsEntity.cpp
+++ b/src/CyclopsEntity.cpp
@@ -1,370 +1,370 @@
#include "CyclopsEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "RockMissileEntity.h"
#include "FallingRockEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
CyclopsEntity::CyclopsEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_CYCLOP), x, y)
{
width = 128;
height = 192;
creatureSpeed = CYCLOP_SPEED[0];
velocity = Vector2D(creatureSpeed);
hp = CYCLOP_HP;
hpDisplay = CYCLOP_HP;
hpMax = CYCLOP_HP;
meleeDamages = CYCLOP_DAMAGES;
type = ENTITY_ENEMY_BOSS;
bloodColor = BloodNone; // stones don't bleed
dyingFrame = 5;
deathFrame = FRAME_CORPSE_CYCLOP;
agonizingSound = SOUND_CYCLOP_DIE;
frame = 0;
if (game().getPlayerPosition().x > x) isMirroring = true;
sprite.setOrigin(64.0f, 143.0f);
nextRockMissile = 0;
destroyLevel = 0;
state = 0;
timer = 2.0f;
counter = 10;
age = -1.5f;
enemyType = EnemyTypeCyclops;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
}
int CyclopsEntity::getHealthLevel()
{
int healthLevel = 0;
if (hp <= hpMax * 0.25) healthLevel = 3;
else if (hp <= hpMax * 0.5) healthLevel = 2;
else if (hp <= hpMax * 0.75) healthLevel = 1;
return healthLevel;
}
void CyclopsEntity::fire()
{
new RockMissileEntity(x, y - 27, nextRockMissile);
SoundManager::getInstance().playSound(SOUND_THROW);
}
void CyclopsEntity::initFallingGrid()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
fallingGrid[i][j] = false;
}
void CyclopsEntity::fallRock()
{
int rx, ry;
do
{
rx = 1 + rand() % (MAP_WIDTH - 2);
ry = 1 + rand() % (MAP_HEIGHT - 2);
}
while (fallingGrid[rx][ry]);
fallingGrid[rx][ry] = true;
new FallingRockEntity(rx * TILE_WIDTH + TILE_WIDTH / 2,
ry * TILE_HEIGHT + TILE_HEIGHT / 2,
rand() % 3,
false);
}
void CyclopsEntity::computeNextRockMissile()
{
if (getHealthLevel() == 0)
nextRockMissile = rand()%5 == 0 ? 1 : 0;
else if (getHealthLevel() == 1)
nextRockMissile = rand()%3 == 0 ? 1 : 0;
else if (getHealthLevel() == 2)
nextRockMissile = rand()%2 == 0 ? 0 : 1;
else
nextRockMissile = rand()%3 == 0 ? 0 : 1;
}
void CyclopsEntity::computeStates(float delay)
{
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0) // walking
{
if (counter > 0)
{
counter--;
timer = 0.5f;
creatureSpeed = CYCLOP_SPEED[getHealthLevel()];
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
}
else
{
velocity.x = 0.0f;
velocity.y = 0.0f;
if (destroyLevel < getHealthLevel())
{
state = 3; // charge to destroy
destroyLevel++;
counter = destroyLevel + 1;
timer = 0.9f;
SoundManager::getInstance().playSound(SOUND_CYCLOP_00);
initFallingGrid();
}
else
{
state = 1; // charge to fire
timer = 0.9f;
counter = CYCLOP_NUMBER_ROCKS[getHealthLevel()];
SoundManager::getInstance().playSound(SOUND_CYCLOP_00);
computeNextRockMissile();
}
}
}
else if (state == 1) // fire
{
state = 2;
fire();
timer = CYCLOP_FIRE_DELAY[getHealthLevel()];
}
else if (state == 2) // fire end
{
if (counter <= 1)
{
state = 0;
timer = 0.2f;
counter = 10;
}
else
{
counter--;
state = 1;
timer = 0.2f;
computeNextRockMissile();
}
}
else if (state == 3)
{
state = 4; // destroy
timer = 0.2;
game().makeShake(0.4f);
SoundManager::getInstance().playSound(SOUND_CYCLOPS_IMPACT);
for (int i = 0; i < 10 ; i++) fallRock();
}
else if (state == 4)
{
if (counter <= 1)
{
state = 0;
timer = 0.2f;
counter = 10;
}
else
{
counter--;
state = 3;
timer = 0.3f;
}
}
}
}
void CyclopsEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
if (isAgonising)
{
if (h < -0.01f)
{
isDying = true;
game().addCorpse(x, y, deathFrame);
if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
// special states
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
// IA
computeStates(delay);
// collisions
if (canCollide()) testSpriteCollisions();
BaseCreatureEntity::animate(delay);
// old frame (for sound)
int oldFrame = frame;
// current frame
if (state == 0)
{
int r = ((int)(age * 5.0f)) % 4;
if (r == 2) frame = 0;
else if (r == 3) frame = 2;
else frame = r;
if (oldFrame == 1 && frame == 0) SoundManager::getInstance().playSound(SOUND_HEAVY_STEP_00);
else if (oldFrame == 2 && frame == 0) SoundManager::getInstance().playSound(SOUND_HEAVY_STEP_01);
}
else if (state == 1)
{
isMirroring = game().getPlayer()->getX() > x;
frame = timer > 0.5f ? 4 : 3;
}
else if (state == 2)
{
frame = 4;
}
else if (state == 3)
{
frame = 6;
}
else if (state == 4)
{
frame = 7;
}
// frame's mirroring
if (velocity.x > 1.0f)
isMirroring = true;
else if (velocity.x < -1.0f)
isMirroring = false;
z = y + 36;
}
int CyclopsEntity::hurt(StructHurt hurtParam)
{
if (destroyLevel < getHealthLevel()) hurtParam.damage /= 3;
return EnemyEntity::hurt(hurtParam);
}
void CyclopsEntity::calculateBB()
{
boundingBox.left = (int)x - 32;
boundingBox.width = 58;
boundingBox.top = (int)y - 52;
boundingBox.height = 90;
}
void CyclopsEntity::afterWallCollide()
{
}
void CyclopsEntity::collideMapRight()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void CyclopsEntity::collideMapLeft()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void CyclopsEntity::collideMapTop()
{
velocity.y = -velocity.y;
afterWallCollide();
}
void CyclopsEntity::collideMapBottom()
{
velocity.y = -velocity.y;
afterWallCollide();
}
void CyclopsEntity::drop()
{
dropItem(ItemBossHeart);
}
void CyclopsEntity::render(sf::RenderTarget* app)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(8 * width, 0, width, height));
app->draw(sprite);
sprite.setPosition(x, y);
EnemyEntity::render(app);
// stones
if (state == 1 && timer < 0.5f)
{
if (nextRockMissile == 0) // small rock
{
sprite.setTextureRect(sf::IntRect(1152, 0, 64, 64));
if (isMirroring)
sprite.setPosition(x + 60, y + 45);
else
sprite.setPosition(x + 4, y + 45);
}
else // medium rock
{
sprite.setTextureRect(sf::IntRect(1152, 64, 64, 64));
if (isMirroring)
sprite.setPosition(x + 60, y + 33);
else
sprite.setPosition(x + 4, y + 33);
}
app->draw(sprite);
sprite.setPosition(x, y);
}
renderLifeBar(app, tools::getLabel("enemy_cyclops"));
}
void CyclopsEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
- inflictsRecoilTo(entity);
+ inflictsRepulsionTo(entity);
}
-void CyclopsEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void CyclopsEntity::inflictsRepulsionTo(BaseCreatureEntity* targetEntity)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
//Vector2D recoilVector = Vector2D(targetEntity->getX(), targetEntity->getY()).vectorTo(Vector2D(x, y), 450.0f);
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 450.0f);
- targetEntity->giveRecoil(true, recoilVector, 0.5f);
+ targetEntity->giveRepulsion(true, recoilVector, 0.5f);
}
}
diff --git a/src/CyclopsEntity.h b/src/CyclopsEntity.h
index 1872e0f..36ee65e 100644
--- a/src/CyclopsEntity.h
+++ b/src/CyclopsEntity.h
@@ -1,43 +1,43 @@
#ifndef CYCLOPENTITY_H
#define CYCLOPENTITY_H
#include "EnemyEntity.h"
#include "PlayerEntity.h"
class CyclopsEntity : public EnemyEntity
{
public:
CyclopsEntity(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void inflictsRepulsionTo(BaseCreatureEntity* targetEntity) override;
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
void afterWallCollide();
virtual int hurt(StructHurt hurtParam) override;
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void drop();
void computeStates(float delay);
int getHealthLevel();
private:
float timer;
int state;
int counter;
int destroyLevel;
int nextRockMissile;
void computeNextRockMissile();
void fire();
void fallRock();
void initFallingGrid();
bool fallingGrid[MAP_WIDTH][MAP_HEIGHT];
};
#endif // CYCLOPENTITY_H
diff --git a/src/DungeonMap.cpp b/src/DungeonMap.cpp
index c05b8e4..e4a0b07 100644
--- a/src/DungeonMap.cpp
+++ b/src/DungeonMap.cpp
@@ -1,2089 +1,2112 @@
#include "DungeonMap.h"
#include "GameFloor.h"
#include "ItemEntity.h"
#include "ChestEntity.h"
#include "sfml_game/ImageManager.h"
#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include "WitchBlastGame.h"
DungeonMap::DungeonMap(int width, int height) : GameMap(width, height)
{
}
DungeonMap::DungeonMap(GameFloor* gameFloor, int x, int y) : GameMap(MAP_WIDTH, MAP_HEIGHT)
{
this->gameFloor = gameFloor;
this->x = x;
this->y = y;
cleared = false;
revealed = true;
visited = false;
known = false;
for (int i = 0; i < NB_RANDOM_TILES_IN_ROOM; i++)
{
randomTileElements[i].type = -1;
randomTileElements[i].x = 0;
randomTileElements[i].y = 0;
randomTileElements[i].rotation = 0;
}
}
DungeonMap::~DungeonMap()
{
}
bool DungeonMap::isVisited()
{
return visited;
}
void DungeonMap::setVisited(bool b)
{
visited = b;
}
bool DungeonMap::isKnown()
{
return known;
}
void DungeonMap::setKnown(bool b)
{
known = b;
}
bool DungeonMap::isRevealed()
{
return revealed;
}
void DungeonMap::setRevealed(bool b)
{
revealed = b;
if (revealed) known = true;
}
bool DungeonMap::isCleared()
{
return cleared;
}
void DungeonMap::setCleared(bool b)
{
cleared = b;
}
roomTypeEnum DungeonMap::getRoomType()
{
return roomType;
}
void DungeonMap::setRoomType(roomTypeEnum roomType)
{
this->roomType = roomType;
if (roomType == roomTypeSecret)
revealed = false;
}
int DungeonMap::getObjectTile(int x, int y)
{
return objectsMap[x][y];
}
logicalMapStateEnum DungeonMap::getLogicalTile(int x, int y)
{
return logicalMap[x][y];
}
void DungeonMap::setObjectTile(int x, int y, int n)
{
objectsMap[x][y] = n;
hasChanged = true;
}
void DungeonMap::setLogicalTile(int x, int y, logicalMapStateEnum state)
{
logicalMap[x][y] = state;
hasChanged = true;
}
int DungeonMap::getFloorOffset()
{
return floorOffset;
}
int DungeonMap::getWallType()
{
return wallType;
}
void DungeonMap::setFloorOffset(int n)
{
floorOffset = n;
}
void DungeonMap::setWallType(int n)
{
wallType = n;
}
doorEnum DungeonMap::getDoorType(int direction)
{
return doorType[direction];
}
void DungeonMap::setDoorType(int direction, doorEnum type)
{
doorType[direction] = type;
}
std::list<DungeonMap::itemListElement> DungeonMap::getItemList()
{
return (itemList);
}
std::list<DungeonMap::chestListElement> DungeonMap::getChestList()
{
return (chestList);
}
std::list<DungeonMap::spriteListElement> DungeonMap::getSpriteList()
{
return (spriteList);
}
DungeonMap::RandomTileElement DungeonMap::getRandomTileElement(int n)
{
if (n < 0 || n >= NB_RANDOM_TILES_IN_ROOM) return RandomTileElement { -1, 0, 0, 0};
return randomTileElements[n];
}
void DungeonMap::setRandomTileElement (int n, RandomTileElement rt)
{
if (n < 0 || n >= NB_RANDOM_TILES_IN_ROOM) return;
randomTileElements[n] = rt;
hasChanged = true;
}
void DungeonMap::displayToConsole()
{
for (int j=0; j < MAP_HEIGHT; j++)
{
for (int i=0; i < MAP_WIDTH; i++)
{
printf("%d", map[i][j]);
}
printf("\n");
}
printf("\n");
}
bool DungeonMap::isDownBlocking(int x, int y)
{
if (!inMap(x, y)) return false;
return (logicalMap[x][y] != LogicalFloor);
}
bool DungeonMap::isUpBlocking(int x, int y)
{
if (!inMap(x, y)) return false;
return (logicalMap[x][y] != LogicalFloor);
}
bool DungeonMap::isLeftBlocking(int x, int y)
{
if (!inMap(x, y)) return false;
return (logicalMap[x][y] != LogicalFloor);
}
bool DungeonMap::isRightBlocking(int x, int y)
{
if (!inMap(x, y)) return false;
return (logicalMap[x][y] != LogicalFloor);
}
bool DungeonMap::isWalkable(int x, int y)
{
if (!inMap(x, y)) return true;
if (roomType == roomTypeKey && !cleared)
{
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
if (x >= x0 - 1 && x <= x0 +1 && y >= y0 - 1 && y <= y0 + 1)
return false;
}
return (logicalMap[x][y] == LogicalFloor);
}
bool DungeonMap::isFlyable(int x, int y)
{
if (x < 0) return true;
if (x > MAP_WIDTH - 1) return true;
if (y < 0) return true;
if (y > MAP_HEIGHT - 1) return true;
return (logicalMap[x][y] != LogicalWall);
}
bool DungeonMap::isShootable(int x, int y)
{
if (!inMap(x, y)) return true;
return (logicalMap[x][y] != LogicalWall && logicalMap[x][y] != LogicalObstacle);
}
bool DungeonMap::containsHealth()
{
ItemList::iterator it;
for (it = itemList.begin (); it != itemList.end ();)
{
itemListElement ilm = *it;
it++;
if (ilm.type >= ItemHealthVerySmall && ilm.type <= ItemHealthVerySmallPoison)
return true;
}
return false;
}
void DungeonMap::randomize(int n)
{
int i, j;
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
initRoom();
// bonus
if (n == 5)
{
roomType = roomTypeBonus;
}
// others
else if (n > 0)
{
int r = rand() % 4;
if (r == 0) // corner blocks
{
map[1][1] = 4;
map[1][MAP_HEIGHT -2] = 4;
map[MAP_WIDTH - 2][1] = 4;
map[MAP_WIDTH - 2][MAP_HEIGHT -2] = 4;
}
else if (r == 1) // bloc in the middle
{
for (i = x0-1; i <= x0+1; i++)
for (j = y0-1; j <= y0+1; j++)
map[i][j] = 4;
}
else if (r == 2) // checker
{
for (i = 2; i < MAP_WIDTH - 2; i = i + 2)
for (j = 2; j < MAP_HEIGHT - 2; j = j + 2)
map[i][j] = 4;
}
cleared = false;
roomType = (roomTypeEnum)(rand() % 3);
}
else
{
cleared = true;
}
}
int DungeonMap::hasNeighbourLeft()
{
if (x > 0 && gameFloor->getRoom(x-1, y) > 0)
{
if (gameFloor->getRoom(x-1, y) == roomTypeBoss) return 2;
else return 1;
}
return 0;
}
int DungeonMap::hasNeighbourRight()
{
if (x < MAP_WIDTH -1 && gameFloor->getRoom(x+1, y) > 0)
{
if (gameFloor->getRoom(x+1, y) == roomTypeBoss) return 2;
else return 1;
}
return 0;
}
int DungeonMap::hasNeighbourUp()
{
if (y > 0 && gameFloor->getRoom(x, y-1) > 0)
{
if (gameFloor->getRoom(x, y-1) == roomTypeBoss) return 2;
else return 1;
}
return 0;
}
int DungeonMap::hasNeighbourDown()
{
if (y < MAP_HEIGHT -1 && gameFloor->getRoom(x, y+1) > 0)
{
if (gameFloor->getRoom(x, y+1) == roomTypeBoss) return 2;
else return 1;
}
return 0;
}
roomTypeEnum DungeonMap::getNeighbourLeft()
{
if (x > 0) return gameFloor->getRoom(x - 1, y);
else return roomTypeNULL;
}
bool DungeonMap::hasKnownNeighbour(int direction, bool mustBeRevealed)
{
switch (direction)
{
case North:
if (y > 0 && gameFloor->getRoom(x, y - 1) > 0)
{
if (mustBeRevealed)
return (gameFloor->getRoom(x, y - 1) != roomTypeSecret || gameFloor->getMap(x, y - 1)->isRevealed());
else
return (gameFloor->getRoom(x, y - 1) != roomTypeSecret || gameFloor->getMap(x, y - 1)->isKnown());
}
break;
case South:
if (y < MAP_HEIGHT -1 && gameFloor->getRoom(x, y + 1) > 0)
{
if (mustBeRevealed)
return (gameFloor->getRoom(x, y + 1) != roomTypeSecret || gameFloor->getMap(x, y + 1)->isRevealed());
else
return (gameFloor->getRoom(x, y + 1) != roomTypeSecret || gameFloor->getMap(x, y + 1)->isKnown());
}
break;
case West:
if (x > 0 && gameFloor->getRoom(x - 1, y) > 0)
{
if (mustBeRevealed)
return (gameFloor->getRoom(x - 1, y) != roomTypeSecret || gameFloor->getMap(x - 1, y)->isRevealed());
else
return (gameFloor->getRoom(x - 1, y) != roomTypeSecret || gameFloor->getMap(x - 1, y)->isKnown());
}
break;
case East:
if (x < MAP_WIDTH -1 && gameFloor->getRoom(x+1, y) > 0)
{
if (mustBeRevealed)
return (gameFloor->getRoom(x + 1, y) != roomTypeSecret || gameFloor->getMap(x + 1, y)->isRevealed());
else
return (gameFloor->getRoom(x + 1, y) != roomTypeSecret || gameFloor->getMap(x + 1, y)->isKnown());
}
break;
}
return false;
}
roomTypeEnum DungeonMap::getNeighbourRight()
{
if (x < MAP_WIDTH - 1) return gameFloor->getRoom(x + 1, y);
else return roomTypeNULL;
}
roomTypeEnum DungeonMap::getNeighbourUp()
{
if (y > 0) return gameFloor->getRoom(x, y - 1);
else return roomTypeNULL;
}
roomTypeEnum DungeonMap::getNeighbourDown()
{
if (y < MAP_HEIGHT - 1) return gameFloor->getRoom(x, y + 1);
else return roomTypeNULL;
}
int DungeonMap::getDivinity(int x, int y)
{
if (x <= 0 || (x >= MAP_WIDTH - 1) || y <= 0 || (y >= MAP_HEIGHT - 1)) return -1;
if (map[x][y] >= MAP_TEMPLE && map[x][y] < MAP_TEMPLE + NB_DIVINITY)
return (map[x][y] - MAP_TEMPLE);
else
return -1;
}
doorEnum getRandomDoor()
{
return (doorEnum)(DoorStandard_0 + ((game().getLevel() - 1) % 8));
/////
int n = rand() % 12;
switch (game().getLevel())
{
case 1:
if (n < 6) return DoorStandard_0;
else return DoorStandard_1;
case 2:
if (n < 5) return DoorStandard_0;
else if (n < 10) return DoorStandard_1;
else return DoorStandard_2;
case 3:
if (n < 6) return DoorStandard_1;
else return DoorStandard_2;
case 4:
if (n < 5) return DoorStandard_1;
else if (n < 10) return DoorStandard_2;
else return DoorStandard_3;
case 5:
if (n < 6) return DoorStandard_2;
else return DoorStandard_3;
case 6:
if (n < 5) return DoorStandard_2;
else if (n < 10) return DoorStandard_3;
else return DoorStandard_4;
default:
if (n < 6) return DoorStandard_3;
else return DoorStandard_4;
}
return (doorEnum)(rand() % 5);
}
void DungeonMap::initRoom(int floorN, int wallN)
{
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
int i, j;
// style
if (roomType == roomTypeSecret)
floorOffset = 8 * 24;
else if (floorN < 0)
floorOffset = ((game().getLevel() - 1) % 8) * 24 ;
else
floorOffset = floorN;
if (wallN < 0) wallType = ((game().getLevel() - 1) % 8) ;
else wallType = wallN;
int wallOffset = wallType * 24;
// doors
for (i = 0; i < 4; i++) doorType[i] = DoorNone;
// outer walls
map[0][0] = wallOffset + MAP_WALL_7 + rand() % 2;
for ( i = 1 ; i < width -1 ; i++)
{
if (i == width / 2)
{
map[i][0] = wallOffset + MAP_WALL_8 + rand() % 2;
map[i][height - 1] = wallOffset + MAP_WALL_8 + rand() % 2;
}
else if (i < width / 2)
{
map[i][0] = wallOffset + MAP_WALL_87 + rand() % 8;
map[i][height - 1] = wallOffset + MAP_WALL_87 + rand() % 8;
}
else
{
map[i][0] = wallOffset + MAP_WALL_87 + rand() % 8;
map[i][height - 1] = wallOffset + MAP_WALL_87 + rand() % 8;
}
}
map[width - 1][0] = wallOffset + MAP_WALL_7 + rand() % 2;
for ( int i = 1 ; i < height -1 ; i++)
{
if (i == height / 2)
{
map[0][i] = wallOffset + MAP_WALL_8 + rand() % 2;
map[width - 1][i] = wallOffset + MAP_WALL_8 + rand() % 2;
}
else if (i < height / 2)
{
map[0][i] = wallOffset + MAP_WALL_87 + rand() % 8;
map[width - 1][i] = wallOffset + MAP_WALL_87 + rand() % 8;
}
else
{
map[0][i] = wallOffset + MAP_WALL_87 + rand() % 8;
map[width - 1][i] = wallOffset + MAP_WALL_87 + rand() % 8;
}
}
map[0][height - 1] = wallOffset + MAP_WALL_7 + rand() % 2;
map[width - 1][height - 1] = wallOffset + MAP_WALL_7 + rand() % 2;
// floor
for ( j = 1 ; j < height - 1 ; j++)
{
for ( i = 1 ; i < width - 1 ; i++)
{
map[i][j] = floorOffset + rand()%(MAP_NORMAL_FLOOR);
while (map[i][j] == map[i - 1][j] || map[i][j] == map[i][j - 1] || map[i][j] == map[i - 1][j - 1] || map[i][j] == map[i + 1][j - 1])
map[i][j] = floorOffset + rand()%(MAP_NORMAL_FLOOR);
}
}
// objects
for ( j = 0 ; j < height; j++)
{
for ( i = 0 ; i < width; i++)
{
objectsMap[i][j] = 0;
if (i == 0 || j == 0 || i == width - 1 || j == height - 1)
logicalMap[i][j] = LogicalWall;
else
logicalMap[i][j] = LogicalFloor;
}
}
// alternative floor
for (i = 0; i < 8; i++)
{
if (rand() % 2 > 0)
{
map[1 + rand() % (MAP_WIDTH - 2)][1 + rand() % (MAP_HEIGHT - 2)] = floorOffset + 16 + i;
}
}
// alternative walls
for (i = 0; i < 8; i++)
{
if (rand() % 2 > 0)
{
int xTile = 0;
int yTile = 0;
int horizontal = rand() % 2;
if (horizontal == 0)
{
yTile = rand() % 2 == 0 ? 0 : MAP_HEIGHT - 1;
xTile = 1 + rand() % 12;
if (xTile > 6) xTile++;
}
else
{
xTile = rand() % 2 == 0 ? 0 : MAP_WIDTH - 1;
yTile = 1 + rand() % 6;
if (yTile > 3) yTile++;
}
map[xTile][yTile] = i + wallOffset + MAP_WALL_87 + 8;
}
}
// doors ?
if (gameFloor != NULL)
{
if (x > 0 && gameFloor->getRoom(x - 1, y) > 0)
{
if (gameFloor->getRoom(x - 1, y) != roomTypeSecret)
{
map[0][MAP_HEIGHT / 2] = floorOffset;
map[0][MAP_HEIGHT / 2 - 1] = floorOffset;
map[0][MAP_HEIGHT / 2 + 1] = floorOffset;
openDoor(0, y0);
if (roomType == roomTypeBoss || gameFloor->getRoom(x - 1, y) == roomTypeBoss)
{
doorType[West] = DoorBoss;
}
else if (roomType == roomTypeChallenge || gameFloor->getRoom(x - 1, y) == roomTypeChallenge)
{
doorType[West] = DoorChallenge;
}
else if (gameFloor->getMap(x - 1, y)->isVisited())
{
doorType[West] = gameFloor->getMap(x - 1, y)->getDoorType(East);
}
else
{
// secret door
doorType[West] = getRandomDoor();
}
}
else
{
doorType[West] = getRandomDoor();
}
}
if (x < MAP_WIDTH - 1 && gameFloor->getRoom(x + 1, y) > 0)
{
if (gameFloor->getRoom(x + 1, y) != roomTypeSecret)
{
map[MAP_WIDTH - 1][MAP_HEIGHT / 2] = floorOffset;
map[MAP_WIDTH - 1][MAP_HEIGHT / 2 - 1] = floorOffset;
map[MAP_WIDTH - 1][MAP_HEIGHT / 2 + 1] = floorOffset;
openDoor(MAP_WIDTH - 1, y0);
if (roomType == roomTypeBoss || gameFloor->getRoom(x + 1, y) == roomTypeBoss)
{
doorType[East] = DoorBoss;
}
else if (roomType == roomTypeChallenge || gameFloor->getRoom(x + 1, y) == roomTypeChallenge)
{
doorType[East] = DoorChallenge;
}
else if (gameFloor->getMap(x + 1, y)->isVisited())
{
doorType[East] = gameFloor->getMap(x + 1, y)->getDoorType(West);
}
else
{
doorType[East] = getRandomDoor();
}
}
else
{
// secret door
doorType[East] = getRandomDoor();
}
}
if (y > 0 && gameFloor->getRoom(x, y - 1) > 0)
{
if (gameFloor->getRoom(x, y - 1) != roomTypeSecret)
{
map[MAP_WIDTH / 2][0] = floorOffset;
map[MAP_WIDTH / 2 - 1][0] = floorOffset;
map[MAP_WIDTH / 2 + 1][0] = floorOffset;
openDoor(x0, 0);
if (roomType == roomTypeBoss || gameFloor->getRoom(x, y - 1) == roomTypeBoss)
{
doorType[North] = DoorBoss;
}
else if (roomType == roomTypeChallenge || gameFloor->getRoom(x, y - 1) == roomTypeChallenge)
{
doorType[North] = DoorChallenge;
}
else if (gameFloor->getMap(x, y - 1)->isVisited())
{
doorType[North] = gameFloor->getMap(x, y - 1)->getDoorType(South);
}
else
{
doorType[North] = getRandomDoor();
}
}
else
{
// secret door
doorType[North] = getRandomDoor();
}
}
if (y < MAP_HEIGHT -1 && gameFloor->getRoom(x, y + 1) > 0)
{
if (gameFloor->getRoom(x, y + 1) != roomTypeSecret)
{
map[MAP_WIDTH / 2][MAP_HEIGHT - 1] = floorOffset;
map[MAP_WIDTH / 2 - 1][MAP_HEIGHT - 1] = floorOffset;
map[MAP_WIDTH / 2 + 1][MAP_HEIGHT - 1] = floorOffset;
openDoor(x0, MAP_HEIGHT -1);
if (roomType == roomTypeBoss || gameFloor->getRoom(x, y + 1) == roomTypeBoss)
{
doorType[South] = DoorBoss;
}
else if (roomType == roomTypeChallenge || gameFloor->getRoom(x, y + 1) == roomTypeChallenge)
{
doorType[South] = DoorChallenge;
}
else if (gameFloor->getMap(x, y + 1)->isVisited())
{
doorType[South] = gameFloor->getMap(x, y + 1)->getDoorType(North);
}
else
{
doorType[South] = getRandomDoor();
}
}
else
{
// secret door
doorType[South] = getRandomDoor();
}
}
if (game().getLevel() > 1 && roomType == roomTypeStarting)
{
doorType[South] = getRandomDoor();
}
else if (roomType == roomTypeExit && game().getLevel() < LAST_LEVEL)
{
doorType[North] = DoorExit;
}
}
}
void DungeonMap::addDestroyableObject(int x, int y, int obj)
{
// out of map
if (x < 0 || x >= MAP_WIDTH) return;
if (y < 0 || y >= MAP_HEIGHT) return;
// doors
if (x == MAP_WIDTH / 2 && (y == 1 || y == MAP_HEIGHT - 2)) return;
if (y == MAP_HEIGHT / 2 && (x == 1 || x == MAP_WIDTH - 2)) return;
// wall
if (logicalMap[x][y] != LogicalFloor) return;
objectsMap[x][y] = obj;
logicalMap[x][y] = LogicalDestroyable;
}
void DungeonMap::openDoor(int x, int y)
{
objectsMap[x][y] = MAPOBJ_DOOR_OPEN;
logicalMap[x][y] = LogicalFloor;
}
void DungeonMap::closeDoor(int x, int y)
{
objectsMap[x][y] = MAPOBJ_DOOR_CLOSED;
logicalMap[x][y] = LogicalWall;
}
bool DungeonMap::isDoor(int x, int y)
{
return objectsMap[x][y] == MAPOBJ_DOOR_OPEN || objectsMap[x][y] == MAPOBJ_DOOR_CLOSED;
}
void DungeonMap::makePatternTile(int x, int y)
{
if (map[x][y] < 24 * MAP_NB_FLOORS && (map[x][y] % 24) < 8) map[x][y] += 8;
else map[x][y] = floorOffset + 8 + rand() % 8;
}
void DungeonMap::initPattern(patternEnum n)
{
int i, j;
if (n == PatternSmallChecker)
{
for ( i = 2 ; i < width - 2 ; i++)
for ( j = 2 ; j < height - 2 ; j++)
{
if ((i + j) % 2 == 1) makePatternTile(i, j);
}
}
if (n == PatternBigChecker)
{
for ( i = 1 ; i < width - 1 ; i++)
for ( j = 1 ; j < height - 1 ; j++)
{
if ((i + j) % 2 == 1) makePatternTile(i, j);
}
}
if (n == PatternBorder)
{
for ( i = 1 ; i < width - 1 ; i++)
for ( j = 1 ; j < height - 1 ; j++)
{
if (i == 1 || j == 1 || i == width - 2 || j == height - 2)
makePatternTile(i, j);
}
}
if (n == PatternBigCircle)
{
for ( i = 2 ; i < width - 2 ; i++)
for ( j = 2 ; j < height - 2 ; j++)
{
if (i == 2 || j == 2 || i == width - 3 || j == height - 3)
makePatternTile(i, j);
}
}
if (n == PatternSmallCircle || n == PatternSmallStar)
{
for ( i = 5 ; i < 10 ; i++)
for ( j = 2 ; j < height - 2 ; j++)
{
if (i == 5 || i == 9 || j == 2 || j == height - 3)
makePatternTile(i, j);
}
}
if (n == PatternSmallStar)
{
makePatternTile(7, 1);
makePatternTile(7, height - 2);
makePatternTile(4, 4);
makePatternTile(10, 4);
}
if (n == PatternSmallDisc)
{
for ( i = 5 ; i < 10 ; i++)
for ( j = 2 ; j < height - 2 ; j++)
{
makePatternTile(i, j);
}
}
}
///// SUB-ROOM GENERATION
void DungeonMap::generateCarpet(int x0, int y0, int w, int h, int n)
{
int xf = x0 + w - 1;
int yf = y0 + h - 1;
map[x0][y0] = n;
map[x0][yf] = n + 6;
map[xf][y0] = n + 2;
map[xf][yf] = n + 8;
int i, j;
for (i = x0 + 1; i <= xf - 1; i++)
{
map[i][y0] = n + 1;
map[i][yf] = n + 7;
for (j = y0 + 1; j <= yf - 1; j++)
map[i][j] = n + 4;
}
for (j = y0 + 1; j <= yf - 1; j++)
{
map[x0][j] = n + 3;
map[xf][j] = n + 5;
}
}
void DungeonMap::generateTable(int x0, int y0, int w, int h, int n)
{
int xf = x0 + w - 1;
int yf = y0 + h - 1;
objectsMap[x0][y0] = n;
objectsMap[x0][yf] = n + 6;
objectsMap[xf][y0] = n + 2;
objectsMap[xf][yf] = n + 8;
int i, j;
for (i = x0 + 1; i <= xf - 1; i++)
{
objectsMap[i][y0] = n + 1;
objectsMap[i][yf] = n + 7;
for (j = y0 + 1; j <= yf - 1; j++)
objectsMap[i][j] = n + 4;
}
for (j = y0 + 1; j <= yf - 1; j++)
{
objectsMap[x0][j] = n + 3;
objectsMap[xf][j] = n + 5;
}
for (i = x0; i <= xf; i++)
for (j = y0; j <= yf; j++)
logicalMap[i][j] = LogicalObstacle;
}
void DungeonMap::generateLongObject(int x0, int y0, int w, int n)
{
int xf = x0 + w - 1;
objectsMap[x0][y0] = n;
logicalMap[x0][y0] = LogicalObstacle;
for (int i = x0 + 1; i <= xf - 1; i++)
{
objectsMap[i][y0] = n + 1;
logicalMap[i][y0] = LogicalObstacle;
}
objectsMap[xf][y0] = n + 2;
logicalMap[xf][y0] = LogicalObstacle;
}
void DungeonMap::generateInselRoom()
{
for (int i = 1; i < MAP_WIDTH - 1; i++)
for (int j = 1; j < MAP_HEIGHT - 1; j++)
{
if (i != MAP_WIDTH / 2 && j != MAP_HEIGHT / 2
&& !(i == MAP_WIDTH / 2 - 1 && j == MAP_HEIGHT / 2 - 1)
&& !(i == MAP_WIDTH / 2 - 1 && j == MAP_HEIGHT / 2 + 1)
&& !(i == MAP_WIDTH / 2 + 1 && j == MAP_HEIGHT / 2 - 1)
&& !(i == MAP_WIDTH / 2 + 1 && j == MAP_HEIGHT / 2 + 1)
)
addHole(i, j);
}
objectsMap[MAP_WIDTH / 2 - 2][MAP_HEIGHT / 2 - 1] = MAPOBJ_WALL_SPECIAL;
objectsMap[MAP_WIDTH / 2 - 1][MAP_HEIGHT / 2 - 2] = MAPOBJ_WALL_SPECIAL;
logicalMap[MAP_WIDTH / 2 - 2][MAP_HEIGHT / 2 - 1] = LogicalObstacle;
logicalMap[MAP_WIDTH / 2 - 1][MAP_HEIGHT / 2 - 2] = LogicalObstacle;
objectsMap[MAP_WIDTH / 2 + 2][MAP_HEIGHT / 2 - 1] = MAPOBJ_WALL_SPECIAL + 1;
objectsMap[MAP_WIDTH / 2 + 1][MAP_HEIGHT / 2 - 2] = MAPOBJ_WALL_SPECIAL + 1;
logicalMap[MAP_WIDTH / 2 + 2][MAP_HEIGHT / 2 - 1] = LogicalObstacle;
logicalMap[MAP_WIDTH / 2 + 1][MAP_HEIGHT / 2 - 2] = LogicalObstacle;
objectsMap[MAP_WIDTH / 2 - 2][MAP_HEIGHT / 2 + 1] = MAPOBJ_WALL_SPECIAL + 2;
objectsMap[MAP_WIDTH / 2 - 1][MAP_HEIGHT / 2 + 2] = MAPOBJ_WALL_SPECIAL + 2;
logicalMap[MAP_WIDTH / 2 - 2][MAP_HEIGHT / 2 + 1] = LogicalObstacle;
logicalMap[MAP_WIDTH / 2 - 1][MAP_HEIGHT / 2 + 2] = LogicalObstacle;
objectsMap[MAP_WIDTH / 2 + 2][MAP_HEIGHT / 2 + 1] = MAPOBJ_WALL_SPECIAL + 3;
objectsMap[MAP_WIDTH / 2 + 1][MAP_HEIGHT / 2 + 2] = MAPOBJ_WALL_SPECIAL + 3;
logicalMap[MAP_WIDTH / 2 + 2][MAP_HEIGHT / 2 + 1] = LogicalObstacle;
logicalMap[MAP_WIDTH / 2 + 1][MAP_HEIGHT / 2 + 2] = LogicalObstacle;
if (!hasNeighbourUp())
addHole(MAP_WIDTH / 2, 1);
if (!hasNeighbourDown())
addHole(MAP_WIDTH / 2, MAP_HEIGHT - 2);
if (!hasNeighbourLeft())
for (int i = 0; i < 4; i++)
{
addHole(1 + i, MAP_HEIGHT / 2);
addHole(1 + i, MAP_HEIGHT / 2 + 1);
}
if (!hasNeighbourRight())
for (int i = 0; i < 4; i++)
{
addHole(MAP_WIDTH - 2 - i, MAP_HEIGHT / 2);
addHole(MAP_WIDTH - 2 - i, MAP_HEIGHT / 2 + 1);
}
addHole(MAP_WIDTH / 2 - 1 , MAP_HEIGHT - 2);
addHole(MAP_WIDTH / 2 + 1 , MAP_HEIGHT - 2);
addHole(MAP_WIDTH / 2 - 2 , MAP_HEIGHT - 3);
addHole(MAP_WIDTH / 2 + 2 , MAP_HEIGHT - 3);
if (rand() % 2 == 0) makePatternTile(MAP_WIDTH / 2, MAP_HEIGHT / 2);
}
void DungeonMap::generateTemple(int x, int y, enumDivinityType type)
{
map[x][y] = MAP_TEMPLE + (int)type;
addHole(x - 1, y - 2);
addHole(x - 1, y - 1);
addHole(x - 1, y);
addHole(x + 1, y - 2);
addHole(x + 1, y - 1);
addHole(x + 1, y );
objectsMap[x][y - 2] = MAPOBJ_TEMPLE_WALL + (int)type;
objectsMap[x][y - 1] = MAPOBJ_TEMPLE_WALL + 10 + (int)type;
logicalMap[x][y - 2] = LogicalObstacle;
logicalMap[x][y - 1] = LogicalObstacle;
}
///// ROOMS GENERATION
Vector2D DungeonMap::generateBonusRoom()
{
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
initRoom();
if (game().getLevel() == 1 || rand() % 3 > 0)
{
if (rand() % 3 == 0)
{
if (rand() % 2 == 0) initPattern(PatternSmallDisc);
else initPattern(PatternSmallStar);
}
objectsMap[x0 - 1][y0 - 1] = MAPOBJ_WALL_SPECIAL;
objectsMap[x0 - 1][y0 + 1] = MAPOBJ_WALL_SPECIAL + 2;
objectsMap[x0 + 1][y0 - 1] = MAPOBJ_WALL_SPECIAL + 1;
objectsMap[x0 + 1][y0 + 1] = MAPOBJ_WALL_SPECIAL + 3;
logicalMap[x0 - 1][y0 - 1] = LogicalObstacle;
logicalMap[x0 - 1][y0 + 1] = LogicalObstacle;
logicalMap[x0 + 1][y0 - 1] = LogicalObstacle;
logicalMap[x0 + 1][y0 + 1] = LogicalObstacle;
if (rand() % 2 == 0)
{
if (rand() % 3 > 0) addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 > 0) addDestroyableObject(1, 1, MAPOBJ_BARREL);
if (rand() % 3 > 0) addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 > 0) addDestroyableObject(MAP_WIDTH - 2, 1, MAPOBJ_BARREL);
}
}
else
{
generateInselRoom();
}
generateRandomTiles();
return (Vector2D(x0 * TILE_WIDTH + TILE_WIDTH / 2, y0 * TILE_HEIGHT + TILE_HEIGHT / 2));
}
Vector2D DungeonMap::generateChestRoom()
{
return generateBonusRoom();
}
void DungeonMap::generateTempleRoom()
{
initRoom();
int x0 = MAP_WIDTH / 2;
int y0 = 1 + MAP_HEIGHT / 2;
+ int playerDivinity = (game().getPlayer()->getDivinity().divinity > -1);
+ bool threeTemple = rand() % 2 == 0;
+ bool forcePlayerDivinity = playerDivinity >= 0 && rand() % 2 == 0;
+
if (rand() % 3 == 0)
{
if (rand() % 2 == 0) initPattern(PatternSmallDisc);
else initPattern(PatternSmallStar);
}
int d0, d1, d2;
- d0 = rand() % NB_DIVINITY;
- d1 = d0;
- while (d0 == d1) d1 = rand() % NB_DIVINITY;
- if (rand() % 2 == 0)
+ if (threeTemple)
{
- // three temple
- d2 = d0;
- while (d0 == d2 || d1 == d2) d2 = rand() % NB_DIVINITY;
+ bool ok = false;
+ while (!ok)
+ {
+ d0 = rand() % NB_DIVINITY;
+ d1 = d0;
+ while (d0 == d1) d1 = rand() % NB_DIVINITY;
+ d2 = d0;
+ while (d0 == d2 || d1 == d2) d2 = rand() % NB_DIVINITY;
+
+ ok = true;
+ if (forcePlayerDivinity)
+ if (d0 != playerDivinity && d1 != playerDivinity && d2 != playerDivinity)
+ ok = false;
+ }
generateTemple(x0 - 2, y0, (enumDivinityType)d0);
generateTemple(x0 + 2, y0, (enumDivinityType)d1);
generateTemple(x0, y0, (enumDivinityType)d2);
}
else
{
- // two temples
+ bool ok = false;
+ while (!ok)
+ {
+ d0 = rand() % NB_DIVINITY;
+ d1 = d0;
+ while (d0 == d1) d1 = rand() % NB_DIVINITY;
+
+ ok = true;
+ if (forcePlayerDivinity)
+ if (d0 != playerDivinity && d1 != playerDivinity)
+ ok = false;
+ }
generateTemple(x0 - 1, y0, (enumDivinityType)d0);
generateTemple(x0 + 1, y0, (enumDivinityType)d1);
}
if (rand() % 2 == 0)
{
addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL);
addDestroyableObject(2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
}
else
{
addDestroyableObject(MAP_WIDTH - 3, MAP_HEIGHT - 2, MAPOBJ_BARREL);
addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
}
}
void DungeonMap::generateSecretRoom()
{
initRoom();
if (rand() % 2 == 0)
{
initPattern(PatternSmallChecker);
}
addDestroyableObject(MAP_WIDTH / 2 - 2, MAP_HEIGHT / 2 - 1, MAPOBJ_SKULL);
addDestroyableObject(MAP_WIDTH / 2 + 2, MAP_HEIGHT / 2 - 1, MAPOBJ_SKULL);
addDestroyableObject(MAP_WIDTH / 2 - 2, MAP_HEIGHT / 2 + 1, MAPOBJ_SKULL);
addDestroyableObject(MAP_WIDTH / 2 + 2, MAP_HEIGHT / 2 + 1, MAPOBJ_SKULL);
generateRandomTiles();
}
Vector2D DungeonMap::generateMerchantRoom()
{
initRoom();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
for (int i = x0 - 3; i <= x0 + 3; i++)
{
if (i == x0 - 3) objectsMap[i][y0] = MAPOBJ_SHOP_LEFT;
else if (i == x0 + 3) objectsMap[i][y0] = MAPOBJ_SHOP_RIGHT;
else objectsMap[i][y0] = MAPOBJ_SHOP;
logicalMap[i][y0] = LogicalObstacle;
}
if (!hasNeighbourUp())
{
objectsMap[x0 - 1][0] = MAPOBJ_PNW;
objectsMap[x0][0] = MAPOBJ_PNW + 1;
objectsMap[x0 + 1][0] = MAPOBJ_PNW + 2;
}
else
{
objectsMap[x0 - 1][MAP_HEIGHT - 1] = MAPOBJ_PNW +3;
objectsMap[x0][MAP_HEIGHT - 1] = MAPOBJ_PNW + 4;
objectsMap[x0 + 1][MAP_HEIGHT - 1] = MAPOBJ_PNW + 5;
}
generateRandomTiles();
if (rand() % 2 == 0)
{
if (rand() % 3 == 0) addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(1, 1, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(MAP_WIDTH - 2, 1, MAPOBJ_BARREL);
}
return (Vector2D(x0 * TILE_WIDTH + TILE_WIDTH / 2, y0 * TILE_HEIGHT + TILE_HEIGHT / 2));
}
Vector2D DungeonMap::generateKeyRoom()
{
initRoom();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
objectsMap[x0 - 1][y0 - 1] = MAPOBJ_WALL_SPECIAL;
objectsMap[x0 - 1][y0 + 1] = MAPOBJ_WALL_SPECIAL + 2;
objectsMap[x0 + 1][y0 - 1] = MAPOBJ_WALL_SPECIAL + 1;
objectsMap[x0 + 1][y0 + 1] = MAPOBJ_WALL_SPECIAL + 3;
logicalMap[x0 - 1][y0 - 1] = LogicalObstacle;
logicalMap[x0 - 1][y0 + 1] = LogicalObstacle;
logicalMap[x0 + 1][y0 - 1] = LogicalObstacle;
logicalMap[x0 + 1][y0 + 1] = LogicalObstacle;
if (rand() % 3 == 0)
{
if (rand() % 2 == 0) initPattern(PatternSmallCircle);
else initPattern(PatternSmallStar);
}
generateCarpet(x0 - 1, y0 - 1, 3, 3, MAP_CARPET);
generateRandomTiles();
return (Vector2D(x0 * TILE_WIDTH + TILE_WIDTH / 2, y0 * TILE_HEIGHT + TILE_HEIGHT / 2));
}
void DungeonMap::generateExitRoom()
{
initRoom();
int x0 = MAP_WIDTH / 2;
if (game().getLevel() < LAST_LEVEL)
{
map[x0][0] = floorOffset;
map[x0 - 1][0] = floorOffset;
map[x0 + 1][0] = floorOffset;
logicalMap[x0][0] = LogicalFloor;
}
if (rand() % 3 == 0) initPattern(PatternBorder);
generateRandomTiles();
if (rand() % 2 == 0)
{
if (rand() % 3 == 0) addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(1, 1, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
if (rand() % 3 == 0) addDestroyableObject(MAP_WIDTH - 2, 1, MAPOBJ_BARREL);
}
}
void DungeonMap::generateRoomRandom(int type)
{
if (rand() % 3 == 0) generateRoomWithHoles(type);
else generateRoomWithoutHoles(type);
}
void DungeonMap::generateRoomWithoutHoles(int type)
{
initRoom();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
int i, j, r;
if (type <= 0) // empty room
{
if (roomType == roomTypeStarting)
{
generateCarpet(5, 3, 5, 3, MAP_CARPET);
if (game().getLevel() > 1)
{
map[x0][MAP_HEIGHT - 1] = floorOffset;
}
}
else if (roomType == roomTypeBoss)
{
if (game().getLevel() == 1) // butcher
{
addDestroyableObject(1, 1, MAPOBJ_BARREL);
addDestroyableObject(1, 2, MAPOBJ_BARREL);
addDestroyableObject(2, 1, MAPOBJ_BARREL);
addDestroyableObject(2, 2, MAPOBJ_BARREL);
}
else if (game().getLevel() == 2) // giant slime
{
objectsMap[1][1] = MAPOBJ_GRID;
objectsMap[1][MAP_HEIGHT -2] = MAPOBJ_GRID;
objectsMap[MAP_WIDTH - 2][1] = MAPOBJ_GRID;
objectsMap[MAP_WIDTH - 2][MAP_HEIGHT -2] = MAPOBJ_GRID;
}
}
if (roomType == roomTypeStandard)
{
bool corr = false;
if (rand() % 3 > 0 && gameFloor->neighboorCount(x, y) > 1)
{
if (type == 0 && game().getLevel() < 5)
{
generateCorridors();
corr = true;
if (rand() % 2 == 0)
{
for (int i = 0; i < 5; i++)
{
int objX = 2 + rand() % (MAP_WIDTH - 4);
int objY = 2 + rand() % (MAP_HEIGHT - 4);
int type = (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP;
if (rand() % 5 == 0) type = MAPOBJ_BARREL;
addDestroyableObject(objX, objY, type);
}
}
}
}
else
{
if (rand() % 3 == 0) initPattern((patternEnum)(rand() % 4));
}
if (!corr) // some barrels
{
if (rand() % 2 == 0)
{
for (int i = 0; i < 10; i++)
{
int objX = 2 + rand() % (MAP_WIDTH - 4);
int objY = 2 + rand() % (MAP_HEIGHT - 4);
int type = MAPOBJ_BARREL_NO_DROP;
if (rand() % 2 == 0) type += (rand() % 2 == 0) ? 1 : 2;
if (rand() % 5 == 0) type = MAPOBJ_BARREL;
addDestroyableObject(objX, objY, type);
}
}
else
{
for (int i = 2; i < MAP_WIDTH - 3; i++)
{
addDestroyableObject(i, 2, (rand() % 4) % 3 + (rand() % 5 == 0) ? MAPOBJ_BARREL_EXPL : MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(i, MAP_HEIGHT - 3, (rand() % 4) % 3 + (rand() % 5 == 0) ? MAPOBJ_BARREL_EXPL : MAPOBJ_BARREL_NO_DROP);
}
for (int i = 3; i < MAP_HEIGHT - 4; i++)
{
addDestroyableObject(2, i, (rand() % 4) % 3 + (rand() % 5 == 0) ? MAPOBJ_BARREL_EXPL : MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(MAP_WIDTH - 3, i, (rand() % 4) % 3 + (rand() % 5 == 0) ? MAPOBJ_BARREL_EXPL : MAPOBJ_BARREL_NO_DROP);
}
}
}
}
}
if (type == 1) // corner block
{
if (rand() % 3 == 0) initPattern(PatternSmallChecker);
int wallOffset = wallType * 24;
map[0][0] = MAP_WALL_X;
map[0][1] = wallOffset + MAP_WALL_7;
map[1][1] = wallOffset + MAP_WALL_77;
map[1][0] = wallOffset + MAP_WALL_7;
logicalMap[1][1] = LogicalWall;
map[0][MAP_HEIGHT - 1] = MAP_WALL_X;
map[0][MAP_HEIGHT - 2] = wallOffset + MAP_WALL_7;
map[1][MAP_HEIGHT - 2] = wallOffset + MAP_WALL_77;
map[1][MAP_HEIGHT - 1] = wallOffset + MAP_WALL_7;
logicalMap[1][MAP_HEIGHT -2] = LogicalWall;
map[MAP_WIDTH - 1][0] = MAP_WALL_X;
map[MAP_WIDTH - 1][1] = wallOffset + MAP_WALL_7;
map[MAP_WIDTH - 2][1] = wallOffset + MAP_WALL_77;
map[MAP_WIDTH - 2][0] = wallOffset + MAP_WALL_7;
logicalMap[MAP_WIDTH - 2][1] = LogicalWall;
map[MAP_WIDTH - 1][MAP_HEIGHT -1] = MAP_WALL_X;
map[MAP_WIDTH - 1][MAP_HEIGHT -2] = wallOffset + MAP_WALL_7;
map[MAP_WIDTH - 2][MAP_HEIGHT -2] = wallOffset + MAP_WALL_77;
map[MAP_WIDTH - 2][MAP_HEIGHT -1] = wallOffset + MAP_WALL_7;
logicalMap[MAP_WIDTH - 2][MAP_HEIGHT -2] = LogicalWall;
if (rand() % 4 == 0)
{
// some barrels in the middle
addDestroyableObject(MAP_WIDTH / 2 - 1, MAP_HEIGHT, MAPOBJ_BARREL);
addDestroyableObject(MAP_WIDTH / 2, MAP_HEIGHT, MAPOBJ_BARREL);
addDestroyableObject(MAP_WIDTH / 2 + 1, MAP_HEIGHT, MAPOBJ_BARREL);
}
}
if (type == 2) // blocks in the middle
{
if (rand() % 3 == 0) initPattern(PatternBorder);
r = 1 + rand() % 3;
generateTable(x0 - r, y0 - 1, 1 + 2 * r, 3, MAPOBJ_BIG_OBSTACLE);
if (rand() / 3 == 0)
{
r = rand() % 4;
switch (r)
{
case 0: addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL); break;
case 1: addDestroyableObject(1, 1, MAPOBJ_BARREL); break;
case 2: addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL); break;
case 3: addDestroyableObject(MAP_WIDTH - 2, 1, MAPOBJ_BARREL); break;
}
}
}
if (type == 3)
{
// big blocks in the corners
generateTable(2, 2, 2, 2, MAPOBJ_BIG_OBSTACLE);
generateTable(2, MAP_HEIGHT - 4, 2, 2, MAPOBJ_BIG_OBSTACLE);
generateTable(MAP_WIDTH - 4, MAP_HEIGHT - 4, 2, 2, MAPOBJ_BIG_OBSTACLE);
generateTable(MAP_WIDTH - 4, 2, 2, 2, MAPOBJ_BIG_OBSTACLE);
}
if (type == 4)
{
// objects
if (rand() % 2 == 0)
{
// type 1
if (rand() % 3 == 0) initPattern(PatternSmallChecker);
bool leftOriented = rand() % 2 == 0;
int bankType = rand() % 4;
if (bankType == 2)
{
int xPlayer = game().getPlayerPosition().x;
if (xPlayer > GAME_WIDTH / 2 - TILE_WIDTH && xPlayer < GAME_WIDTH / 2 + TILE_WIDTH)
bankType = rand() % 2;
}
int x0 = leftOriented ? 5 : 3;
if (leftOriented)
{
objectsMap[2][4] = MAPOBJ_CHURCH_FURN_L;
logicalMap[2][4] = LogicalObstacle;
if (rand() % 2 == 0) addDestroyableObject(1, 1, MAPOBJ_BARREL);
else addDestroyableObject(1, MAP_HEIGHT - 2, MAPOBJ_BARREL);
}
else
{
objectsMap[12][4] = MAPOBJ_CHURCH_FURN_R;
logicalMap[12][4] = LogicalObstacle;
if (rand() % 2 == 0) addDestroyableObject(MAP_WIDTH - 2, 1, MAPOBJ_BARREL);
else addDestroyableObject(MAP_WIDTH - 2, MAP_HEIGHT - 2, MAPOBJ_BARREL);
}
for (int i = 0; i < 3; i++)
{
int xPos = x0 + i * 3;
switch (bankType)
{
case 0:
objectsMap[xPos][2] = MAPOBJ_BANK_TOP;
objectsMap[xPos][3] = MAPOBJ_BANK_BOTTOM;
objectsMap[xPos][5] = MAPOBJ_BANK_TOP;
objectsMap[xPos][6] = MAPOBJ_BANK_BOTTOM;
for (int j = 2; j <= 6; j++) if (j != 4) logicalMap[xPos][j] = LogicalObstacle;
break;
case 1:
objectsMap[xPos][2] = MAPOBJ_BANK_TOP;
objectsMap[xPos][3] = MAPOBJ_BANK;
objectsMap[xPos][4] = MAPOBJ_BANK;
objectsMap[xPos][5] = MAPOBJ_BANK;
objectsMap[xPos][6] = MAPOBJ_BANK_BOTTOM;
for (int j = 2; j <= 6; j++) logicalMap[xPos][j] = LogicalObstacle;
break;
default:
objectsMap[xPos][1] = MAPOBJ_BANK_TOP;
objectsMap[xPos][2] = MAPOBJ_BANK;
objectsMap[xPos][3] = MAPOBJ_BANK_BOTTOM;
objectsMap[xPos][5] = MAPOBJ_BANK_TOP;
objectsMap[xPos][6] = MAPOBJ_BANK;
objectsMap[xPos][7] = MAPOBJ_BANK_BOTTOM;
for (int j = 1; j <= 7; j++) if (j != 4) logicalMap[xPos][j] = LogicalObstacle;
break;
}
}
}
else
{
initPattern(PatternSmallStar);
generateLongObject(2, 2, 3, MAPOBJ_LONG_LEFT);
generateLongObject(2, 6, 3, MAPOBJ_LONG_LEFT);
generateLongObject(10, 2, 3, MAPOBJ_LONG_LEFT);
generateLongObject(10, 6, 3, MAPOBJ_LONG_LEFT);
generateLongObject(6, 4, 3, MAPOBJ_LONG_LEFT);
}
}
if (type == ROOM_TYPE_CHECKER)
{
// "checker"
if (rand() % 3 == 0) initPattern(PatternSmallChecker);
for (i = 2; i < MAP_WIDTH - 2; i = i + 2)
for (j = 2; j < MAP_HEIGHT - 2; j = j + 2)
{
objectsMap[i][j] = game().getLevel() >= 6 ? MAPOBJ_TOMB : MAPOBJ_OBSTACLE;
logicalMap[i][j] = LogicalObstacle;
}
if (game().getLevel() >= 4 || rand() % 4 > 0)
{
int nbBarrels = 6 + rand() % 9;
for (int i = 0; i < nbBarrels; i++)
{
int objX = 2 + rand() % (MAP_WIDTH - 4);
int objY = 2 + rand() % (MAP_HEIGHT - 4);
int type = (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP;
if (rand() % 4 == 0) type = MAPOBJ_BARREL_EXPL;
addDestroyableObject(objX, objY, type);
}
}
}
generateRandomTiles();
}
void DungeonMap::addHole(int x, int y)
{
int n = MAPOBJ_HOLE;
if (y > 0 && logicalMap[x][y - 1] == LogicalHole)
n = MAPOBJ_HOLE_BOTTOM;
else if (logicalMap[x - 1][y] == LogicalHole && logicalMap[x - 1][y - 1] == LogicalHole)
n = MAPOBJ_HOLE_LEFT;
else if (logicalMap[x + 1][y] == LogicalHole && logicalMap[x + 1][y - 1] == LogicalHole)
n = MAPOBJ_HOLE_RIGHT;
objectsMap[x][y] = n;
logicalMap[x][y] = LogicalHole;
if (logicalMap[x][y + 1] == LogicalHole && objectsMap[x - 1][y + 1] == MAPOBJ_HOLE_TOP)
objectsMap[x - 1][y + 1] = MAPOBJ_HOLE_LEFT;
if (logicalMap[x][y + 1] == LogicalHole && objectsMap[x + 1][y + 1] == MAPOBJ_HOLE_TOP)
objectsMap[x + 1][y + 1] = MAPOBJ_HOLE_RIGHT;
}
void DungeonMap::generateRoomWithHoles(int type)
{
initRoom();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
int i, j, r;
if (type == 0)
{
if (roomType == roomTypeStarting)
{
generateCarpet(5, 3, 5, 3, MAP_CARPET);
if (game().getLevel() > 1)
{
map[x0 - 1][MAP_HEIGHT - 1] = 62;
map[x0][MAP_HEIGHT - 1] = 63;
map[x0 + 1][MAP_HEIGHT - 1] = 64;
}
}
/*else if (rand() % 2 == 0)
{
for (int i = 1; i <= MAP_WIDTH - 2; i++)
{
if (i != MAP_WIDTH / 2 || !hasNeighbourUp()) addHole(i, 1);
}
for (int i = 2; i <= MAP_HEIGHT - 3; i++)
{
if (i != MAP_HEIGHT / 2 || !hasNeighbourLeft()) addHole(1, i);
if (i != MAP_HEIGHT / 2 || !hasNeighbourRight()) addHole(MAP_WIDTH - 2, i);
}
addHole(MAP_WIDTH - 2, MAP_HEIGHT - 2);
for (int i = 1; i <= MAP_WIDTH - 3; i++)
{
if (i != MAP_WIDTH / 2 || !hasNeighbourDown()) addHole(i, MAP_HEIGHT - 2);
}
}*/
}
else if (type == 1)
{
// corner hole
if (rand() % 3 == 0) initPattern(PatternSmallChecker);
addHole(1, 1);
addHole(MAP_WIDTH - 2, MAP_HEIGHT -2);
addHole(1, MAP_HEIGHT -2);
addHole(MAP_WIDTH - 2, 1);
if (rand() % 2 == 0)
addDestroyableObject(MAP_WIDTH - 2 + rand() % 5, MAP_HEIGHT - 2 + rand() % 5, MAPOBJ_BARREL_EXPL);
}
else if (type == 2)
{
// blocks in the middle
if (rand() % 3 == 0) initPattern(PatternBorder);
/*if (false) //irregular // TO CORRECT
{
r = 1; //rand() % 2;
for (i = x0 - r; i <= x0 + r; i++)
{
if (rand() % 3 == 0) addHole(i, y0 - 1);
addHole(i, y0);
if (rand() % 3 == 0) addHole(i, y0 + 1);
}
for (j = y0 - 1; j <= y0 + 1; j++)
{
if (rand() % 3 == 0) addHole(x0 - r - 1, j);
if (rand() % 3 == 0) addHole(x0 + r + 1, j);
}
}
else*/
{
r = 1 + rand() % 2;
for (i = x0 - r; i <= x0 + r; i++)
for (j = y0 - 1; j <= y0 + 1; j++)
{
addHole(i, j);
}
}
}
else if (type == 3)
{
// 4 holes
addHole(2, 2);
addHole(2, 3);
addHole(3, 2);
addHole(3, 3);
addHole(MAP_WIDTH - 4, 2);
addHole(MAP_WIDTH - 4, 3);
addHole(MAP_WIDTH - 3, 2);
addHole(MAP_WIDTH - 3, 3);
addHole(2, MAP_HEIGHT - 4);
addHole(2, MAP_HEIGHT - 3);
addHole(3, MAP_HEIGHT - 4);
addHole(3, MAP_HEIGHT - 3);
addHole(MAP_WIDTH - 4, MAP_HEIGHT - 4);
addHole(MAP_WIDTH - 4, MAP_HEIGHT - 3);
addHole(MAP_WIDTH - 3, MAP_HEIGHT - 4);
addHole(MAP_WIDTH - 3, MAP_HEIGHT - 3);
if (rand() % 3 == 0)
{
addDestroyableObject(4, 2, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(4, 3, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(4, MAP_HEIGHT - 4, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(4, MAP_HEIGHT - 3, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(MAP_WIDTH - 5, 2, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(MAP_WIDTH - 5, 3, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(MAP_WIDTH - 5, MAP_HEIGHT - 4, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
addDestroyableObject(MAP_WIDTH - 5, MAP_HEIGHT - 3, (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP);
}
}
else if (type == 4)
{
if (rand() % 2 == 0)
{
// holes and (sometimes) obstacles randomly
if (rand() % 3 == 0) initPattern(PatternBigCircle);
int r = 6 + rand()% 5;
int obstacleType = rand() % 2;
for (int i = 0; i < r; i++)
{
int rx = 1 + rand() % (MAP_WIDTH - 3);
int ry = 1 + rand() % (MAP_HEIGHT - 3);
bool ok = true;
bool isObstacle = (obstacleType == 1) && rand() % 2 == 0;
if ( (rx == 1 && ry == MAP_HEIGHT / 2)
|| (rx == MAP_WIDTH - 2 && ry == MAP_HEIGHT / 2)
|| (rx == MAP_WIDTH /2 && ry == MAP_HEIGHT - 2)
|| (rx == MAP_WIDTH /2 && ry == 1) )
{
ok = false;
}
else
{
for (int ix = -1; ix <= 1; ix++)
for (int iy = -1; iy <= 1; iy++)
{
ok = ok && logicalMap[rx + ix][ry + iy] != LogicalHole;
ok = ok && logicalMap[rx + ix][ry + iy] != LogicalObstacle;
}
}
if (ok)
{
if (!isObstacle)
{
addHole(rx, ry);
}
else
{
objectsMap[rx][ry] = MAPOBJ_OBSTACLE;
logicalMap[rx][ry] = LogicalObstacle;
}
}
else
{
i--;
}
}
}
else
{
// big holes left and right
bool holesNW = rand() % 2 == 0;
for (int i = 0; i < 5; i++)
{
if (holesNW)
{
addHole(1 + i, 1);
addHole(1 + i, 2);
addHole(MAP_WIDTH - 2 - i, MAP_HEIGHT - 3);
addHole(MAP_WIDTH - 2 - i, MAP_HEIGHT - 2);
}
else
{
addHole(1 + i, MAP_HEIGHT - 3);
addHole(1 + i, MAP_HEIGHT - 2);
addHole(MAP_WIDTH - 2 - i, 1);
addHole(MAP_WIDTH - 2 - i, 2);
}
}
}
}
else //if (type == 5)
{
// "checker"
for (i = 2; i < MAP_WIDTH - 2; i = i + 2)
for (j = 2; j < MAP_HEIGHT - 2; j = j + 2)
{
addHole(i, j);
}
if (game().getLevel() >= 4 || rand() % 4 > 0)
{
int nbBarrels = 6 + rand() % 9;
for (int i = 0; i < nbBarrels; i++)
{
int objX = 2 + rand() % (MAP_WIDTH - 4);
int objY = 2 + rand() % (MAP_HEIGHT - 4);
int type = (rand() % 4) % 3 + MAPOBJ_BARREL_NO_DROP;
if (rand() % 4 == 0) type = MAPOBJ_BARREL_EXPL;
addDestroyableObject(objX, objY, type);
}
}
}
generateRandomTiles();
}
void DungeonMap::addItem(int itemType, float x, float y, bool merch)
{
itemListElement ilm;
ilm.type = itemType;
ilm.x = x;
ilm.y = y;
ilm.merch = merch;
itemList.push_back(ilm);
}
void DungeonMap::addSprite(int spriteType, int frame, float x, float y, float scale)
{
spriteListElement slm;
slm.type = spriteType;
slm.frame = frame;
slm.x = x;
slm.y = y;
slm.scale = scale;
spriteList.push_back(slm);
}
void DungeonMap::addChest(int chestType, bool state, float x, float y)
{
chestListElement clm;
clm.type = chestType;
clm.state = state;
clm.x = x;
clm.y = y;
chestList.push_back(clm);
}
void DungeonMap::restoreItems()
{
ItemList::iterator it;
for (it = itemList.begin (); it != itemList.end ();)
{
itemListElement ilm = *it;
it++;
ItemEntity* itemEntity = new ItemEntity((enumItemType)(ilm.type), ilm.x, ilm.y);
itemEntity->setMerchandise(ilm.merch);
}
}
void DungeonMap::restoreSprites()
{
SpriteList::iterator it;
for (it = spriteList.begin (); it != spriteList.end ();)
{
spriteListElement ilm = *it;
it++;
if (ilm.type == ENTITY_BLOOD)
game().getCurrentMapEntity()->addBlood(ilm.x, ilm.y, ilm.frame, ilm.scale);
else if (ilm.type == ENTITY_CORPSE)
game().getCurrentMapEntity()->addCorpse(ilm.x, ilm.y, ilm.frame);
}
}
void DungeonMap::restoreChests()
{
ChestList::iterator it;
for (it = chestList.begin (); it != chestList.end ();)
{
chestListElement clm = *it;
it++;
new ChestEntity(clm.x, clm.y, clm.type, clm.state);
}
}
void DungeonMap::restoreMapObjects()
{
restoreItems();
restoreSprites();
restoreChests();
cleanMapObjects();
}
void DungeonMap::cleanMapObjects()
{
itemList.clear();
spriteList.clear();
chestList.clear();
}
void DungeonMap::addRandomGrids(int n)
{
int counter = n;
while (counter > 0)
{
int rx = 1 + rand() % (MAP_WIDTH - 2);
int ry = 1 + rand() % (MAP_HEIGHT - 2);
if (logicalMap[rx][ry] == LogicalFloor
&& objectsMap[rx][ry] == 0)
{
objectsMap[rx][ry] = MAPOBJ_GRID;
counter--;
}
}
}
void DungeonMap::generateCorridors()
{
int xCor = 1 + rand()% 4;
int yCor = 1 + rand()% 1;
int wallOffset = wallType * 24;
if (!hasNeighbourLeft())
{
for (int i = 0; i < xCor; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
{
map[i][j] = MAP_WALL_X;
logicalMap[i][j] = LogicalWall;
}
}
if (!hasNeighbourRight())
{
for (int i = MAP_WIDTH - 1; i > MAP_WIDTH - 1 - xCor; i--)
for (int j = 0; j < MAP_HEIGHT; j++)
{
map[i][j] = MAP_WALL_X;
logicalMap[i][j] = LogicalWall;
}
}
if (!hasNeighbourUp())
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < yCor; j++)
{
map[i][j] = MAP_WALL_X;
logicalMap[i][j] = LogicalWall;
}
}
if (!hasNeighbourDown())
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = MAP_HEIGHT - 1; j > MAP_HEIGHT - 1 - yCor; j--)
{
map[i][j] = MAP_WALL_X;
logicalMap[i][j] = LogicalWall;
}
}
//
for (int i = 0; i < MAP_WIDTH; i++)
{
for (int j = 0; j < MAP_HEIGHT; j++)
{
if (map[i][j] != MAP_WALL_X)
{
if (getTile(i - 1, j) == MAP_WALL_X)
{
if (j == 0 || getTile(i, j - 1) == MAP_WALL_X)
{
map[i][j] = wallOffset + MAP_WALL_7;
}
else if (j == MAP_HEIGHT - 1 || getTile(i, j + 1) == MAP_WALL_X)
{
map[i][j] = wallOffset + MAP_WALL_7;
}
else
{
if (j < MAP_HEIGHT / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else if (j > MAP_HEIGHT / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else map[i][j] = wallOffset + MAP_WALL_8;
}
logicalMap[i][j] = LogicalWall;
}
else if (getTile(i + 1, j) == MAP_WALL_X)
{
if (j == 0 || getTile(i, j - 1) == MAP_WALL_X)
{
map[i][j] = wallOffset + MAP_WALL_7;
map[i][j] = wallOffset + MAP_WALL_7;
}
else if (j == MAP_HEIGHT - 1 || getTile(i, j + 1) == MAP_WALL_X) map[i][j] = wallOffset + MAP_WALL_7;
else
{
if (j < MAP_HEIGHT / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else if (j > MAP_HEIGHT / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else map[i][j] = wallOffset + MAP_WALL_8;
}
logicalMap[i][j] = LogicalWall;
}
else if (getTile(i, j - 1) == MAP_WALL_X)
{
if (i == 0) map[i][j] = wallOffset + MAP_WALL_7;
else if (i == MAP_WIDTH - 1) map[i][j] = wallOffset + MAP_WALL_7;
else
{
if (i < MAP_WIDTH / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else if (i > MAP_WIDTH / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else map[i][j] = wallOffset + MAP_WALL_8;
}
logicalMap[i][j] = LogicalWall;
}
else if (getTile(i, j + 1) == MAP_WALL_X)
{
if (i == 0) map[i][j] = wallOffset + MAP_WALL_7;
else if (i == MAP_WIDTH - 1) map[i][j] = wallOffset + MAP_WALL_7;
else
{
if (i < MAP_WIDTH / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else if (i > MAP_WIDTH / 2) map[i][j] = wallOffset + MAP_WALL_87 + rand() % 8;
else map[i][j] = wallOffset + MAP_WALL_8;
}
logicalMap[i][j] = LogicalWall;
}
}
}
}
}
void DungeonMap::generateRandomTiles()
{
for (int i = 0; i < NB_RANDOM_TILES_IN_ROOM; i++)
generateRandomTile(i);
}
void DungeonMap::generateRandomTile(int index)
{
bool ok = false;
for (int i = 0; !ok && i < NB_RANDOM_TILE_TRY; i++)
{
int n = rand() % NB_RANDOM_TILES;
int xTile, yTile;
if (randomDungeonTiles[n].canBeOnWall)
{
xTile = rand() % (GAME_WIDTH - randomDungeonTiles[n].width);
yTile = rand() % (GAME_HEIGHT - randomDungeonTiles[n].height);
}
else
{
xTile = TILE_WIDTH + rand() % (GAME_WIDTH - 2 * TILE_WIDTH - randomDungeonTiles[n].width);
yTile = TILE_HEIGHT + rand() % (GAME_HEIGHT - 2 * TILE_HEIGHT - randomDungeonTiles[n].height);
}
int x0 = xTile / TILE_WIDTH;
int xf = (xTile + randomDungeonTiles[n].width) / TILE_WIDTH;
int y0 = yTile / TILE_HEIGHT;
int yf = (yTile + randomDungeonTiles[n].height) / TILE_HEIGHT;
ok = true;
// proceed rarity
if (randomDungeonTiles[n].rarity > 1 && (rand() % randomDungeonTiles[n].rarity) != 0 )
{
ok = false;
}
// each random sprite type should be present no more that once in a room
for (int j = 0; j < index; j++)
{
if (n == randomTileElements[j].type) ok = false;
}
if (!randomDungeonTiles[n].randomPlace || roomType == roomTypeKey)
{
for (int ix = x0; ix <= xf; ix++)
for (int iy = y0; iy <= yf; iy++)
{
ok = ok && isWalkable(ix, iy);
}
}
if (ok && !randomDungeonTiles[n].canBeOnWall)
{
for (int ix = x0; ix <= xf; ix++)
for (int iy = y0; iy <= yf; iy++)
{
ok = ok && isFlyable(ix, iy);
}
}
if (ok)
{
randomTileElements[index].type = n;
randomTileElements[index].x = xTile;
randomTileElements[index].y = yTile;
if (randomDungeonTiles[n].canRotate)
randomTileElements[index].rotation = rand()% 360;
else
randomTileElements[index].rotation = 0;
}
}
}
bool DungeonMap::callRevelation()
{
if (hasNeighbourRight() && !gameFloor->getMap(x + 1, y)->isRevealed())
{
map[MAP_WIDTH - 1][MAP_HEIGHT / 2] = floorOffset;
map[MAP_WIDTH - 1][MAP_HEIGHT / 2 - 1] = floorOffset;
map[MAP_WIDTH - 1][MAP_HEIGHT / 2 + 1] = floorOffset;
if (cleared)
openDoor(MAP_WIDTH - 1, MAP_HEIGHT / 2);
else
closeDoor(MAP_WIDTH - 1, MAP_HEIGHT / 2);
game().setDoorVisible(East);
gameFloor->getMap(x + 1, y)->setRevealed(true);
hasChanged = true;
for (int i = 0; i < 36; i++)
game().generateStar(
(i % 2 == 0) ? sf::Color(50, 50, 255, 255) : sf::Color(200, 200, 255, 255),
(MAP_WIDTH - 1) * TILE_WIDTH + rand() % TILE_WIDTH,
(MAP_HEIGHT / 2 - 1) * TILE_HEIGHT + rand() % (TILE_HEIGHT * 3) );
return true;
}
else if (hasNeighbourLeft() && !gameFloor->getMap(x - 1, y)->isRevealed())
{
map[0][MAP_HEIGHT / 2] = floorOffset;
map[0][MAP_HEIGHT / 2 - 1] = floorOffset;
map[0][MAP_HEIGHT / 2 + 1] = floorOffset;
if (cleared)
openDoor(0, MAP_HEIGHT / 2);
else
closeDoor(0, MAP_HEIGHT / 2);
game().setDoorVisible(West);
gameFloor->getMap(x - 1, y)->setRevealed(true);
hasChanged = true;
for (int i = 0; i < 36; i++)
game().generateStar(
(i % 2 == 0) ? sf::Color(50, 50, 255, 255) : sf::Color(200, 200, 255, 255),
rand() % TILE_WIDTH,
(MAP_HEIGHT / 2 - 1) * TILE_HEIGHT + rand() % (TILE_HEIGHT * 3) );
return true;
}
else if (hasNeighbourUp() && !gameFloor->getMap(x, y - 1)->isRevealed())
{
map[MAP_WIDTH / 2][0] = floorOffset;
map[MAP_WIDTH / 2 - 1][0] = floorOffset;
map[MAP_WIDTH / 2 + 1][0] = floorOffset;
if (cleared)
openDoor(MAP_WIDTH / 2, 0);
else
closeDoor(MAP_WIDTH / 2, 0);
game().setDoorVisible(North);
gameFloor->getMap(x, y - 1)->setRevealed(true);
hasChanged = true;
for (int i = 0; i < 36; i++)
game().generateStar(
(i % 2 == 0) ? sf::Color(50, 50, 255, 255) : sf::Color(200, 200, 255, 255),
(MAP_WIDTH / 2 - 1) * TILE_WIDTH + rand() % (TILE_WIDTH * 3),
rand() % TILE_HEIGHT );
return true;
}
else if (hasNeighbourDown() && !gameFloor->getMap(x, y + 1)->isRevealed())
{
map[MAP_WIDTH / 2][MAP_HEIGHT - 1] = floorOffset;
map[MAP_WIDTH / 2 - 1][MAP_HEIGHT - 1] = floorOffset;
map[MAP_WIDTH / 2 + 1][MAP_HEIGHT - 1] = floorOffset;
if (cleared)
openDoor(MAP_WIDTH / 2, MAP_HEIGHT -1);
else
closeDoor(MAP_WIDTH / 2, MAP_HEIGHT -1);
game().setDoorVisible(South);
gameFloor->getMap(x, y + 1)->setRevealed(true);
hasChanged = true;
for (int i = 0; i < 36; i++)
game().generateStar(
(i % 2 == 0) ? sf::Color(50, 50, 255, 255) : sf::Color(200, 200, 255, 255),
(MAP_WIDTH / 2 - 1) * TILE_WIDTH + rand() % (TILE_WIDTH * 3),
(MAP_HEIGHT - 1) * TILE_HEIGHT + rand() % TILE_HEIGHT );
return true;
}
return false;
}
diff --git a/src/EffectZoneEntity.cpp b/src/EffectZoneEntity.cpp
new file mode 100644
index 0000000..5777b45
--- /dev/null
+++ b/src/EffectZoneEntity.cpp
@@ -0,0 +1,126 @@
+#include "EffectZoneEntity.h"
+#include "BaseCreatureEntity.h"
+#include "ExplosionEntity.h"
+#include "sfml_game/ImageManager.h"
+#include "sfml_game/SoundManager.h"
+#include "sfml_game/SpriteEntity.h"
+#include "Constants.h"
+#include "WitchBlastGame.h"
+
+#include <iostream>
+
+EffectZoneEntity::EffectZoneEntity(float x, float y, bool fromPlayer, float duration,
+ EffectZoneTypeEnum effectZoneType, int damage)
+ : SpriteEntity(ImageManager::getInstance().getImage(IMAGE_EFFECT_ZONE), x, y, 64, 64)
+{
+ type = ENTITY_EFFECT_ZONE;
+ this->effectZoneType = effectZoneType;
+ imagesProLine = 6;
+ lifetime = duration;
+ this->fromPlayer = fromPlayer;
+ this->damage = damage;
+
+ switch (effectZoneType)
+ {
+ case EffectZoneTypeIce: frame = 0; break;
+ case EffectZoneTypePoison: frame = 1; break;
+ case EffectZoneTypeFire: frame = 2; break;
+ }
+ z = 1500; // for flying zones
+
+ randomAngle = 5 + rand() % 15;
+ if (rand() % 2 == 0) randomAngle = -randomAngle;
+}
+
+void EffectZoneEntity::animate(float delay)
+{
+ if (lifetime > 0)
+ {
+ if (age >= lifetime) isDying = true;
+ else if (lifetime - age < 1.0f)
+ {
+ sprite.setColor(sf::Color(255, 255, 255, 255 * (lifetime - age)));
+ }
+ }
+ age += delay;
+ angle += delay * randomAngle;
+
+ testCollisions();
+}
+
+void EffectZoneEntity::render(sf::RenderTarget* app)
+{
+ SpriteEntity::render(app);
+}
+
+void EffectZoneEntity::dying()
+{
+ isDying = true;
+}
+
+void EffectZoneEntity::collideIce(BaseCreatureEntity* entity)
+{
+ entity->hurt(BaseCreatureEntity::getHurtParams(0, ShotTypeIce, 1, false, SourceTypeExplosion, EnemyTypeNone, false));
+}
+
+void EffectZoneEntity::collidePoison(BaseCreatureEntity* entity)
+{
+ if (!entity->getSpecialState(SpecialStatePoison).active)
+ {
+ entity->hurt(BaseCreatureEntity::getHurtParams(0, ShotTypePoison, 2, false, SourceTypeExplosion, EnemyTypeNone, false));
+ }
+}
+
+void EffectZoneEntity::explode()
+{
+ if (damage > 0)
+ {
+ new ExplosionEntity(x, y, ExplosionTypeStandard, damage, EnemyTypeNone, false);
+ damage = 0;
+ lifetime = age + 1.0f;
+ }
+}
+
+void EffectZoneEntity::testCollisions()
+{
+ EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
+ EntityManager::EntityList::iterator it;
+
+ for (it = entityList->begin (); it != entityList->end ();)
+ {
+ GameEntity *e = *it;
+ it++;
+
+ BaseCreatureEntity* entity = dynamic_cast<BaseCreatureEntity*>(e);
+ if (entity != NULL)
+ {
+ bool ok = true;
+
+ if (fromPlayer && entity->getType() == ENTITY_PLAYER) ok = false;
+ if (!fromPlayer && entity->getType() != ENTITY_PLAYER) ok = false;
+
+ if (ok && entity->getHp() > 0 && entity->canCollide())
+ {
+ entity->calculateBB();
+
+ sf::IntRect bb;
+ bb.left = x - 26;
+ bb.width = 52;
+ bb.top = y - 26;
+ bb.height = 52;
+
+ if (bb.intersects(entity->getBoundingBox()))
+ {
+ switch (effectZoneType)
+ {
+ case EffectZoneTypeIce: collideIce(entity); break;
+ case EffectZoneTypePoison: collidePoison(entity); break;
+ case EffectZoneTypeFire: explode(); break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/src/EffectZoneEntity.h b/src/EffectZoneEntity.h
new file mode 100644
index 0000000..8625d00
--- /dev/null
+++ b/src/EffectZoneEntity.h
@@ -0,0 +1,39 @@
+#ifndef EFFECT_ZONE_ENTITY_H
+#define EFFECT_ZONE_ENTITY_H
+
+#include "sfml_game/SpriteEntity.h"
+#include "BaseCreatureEntity.h"
+
+enum EffectZoneTypeEnum
+{
+ EffectZoneTypeIce,
+ EffectZoneTypePoison,
+ EffectZoneTypeFire
+};
+
+class EffectZoneEntity : public SpriteEntity
+{
+ public:
+ EffectZoneEntity(float x, float y, bool fromPlayer, float duration,
+ EffectZoneTypeEnum effectZoneType, int damage = 0);
+
+ virtual void animate(float delay);
+ virtual void render(sf::RenderTarget* app);
+ virtual void dying();
+
+ protected:
+ void testCollisions();
+
+ private:
+ int damage;
+ EffectZoneTypeEnum effectZoneType;
+ enumShotType damageType;
+ bool fromPlayer;
+ float randomAngle;
+
+ void collideIce(BaseCreatureEntity* entity);
+ void collidePoison(BaseCreatureEntity* entity);
+ void explode();
+};
+
+#endif // EFFECT_ZONE_ENTITY_H
diff --git a/src/EnemyBoltEntity.cpp b/src/EnemyBoltEntity.cpp
index ce54300..a9e1d06 100644
--- a/src/EnemyBoltEntity.cpp
+++ b/src/EnemyBoltEntity.cpp
@@ -1,54 +1,63 @@
#include "EnemyBoltEntity.h"
#include "Constants.h"
#include "sfml_game/ImageManager.h"
#include "WitchBlastGame.h"
EnemyBoltEntity::EnemyBoltEntity(float x, float y, enumShotType boltType, int level, enemyTypeEnum enemyType) :
BoltEntity(x, y, -1, boltType, level)
{
damages = INITIAL_BOLT_DAMAGES;
type = ENTITY_ENEMY_BOLT;
this->enemyType = enemyType;
viscosity = 1.0f;
switch (boltType)
{
case ShotTypeIce: frame = 2; break;
case ShotTypeLightning: frame = 5; break;
case ShotTypeFire: frame = 6; break;
case ShotTypeBomb: frame = 8; break;
case ShotTypeIllusion: frame = 3; break;
default: frame = 1; break;
}
setMap(game().getCurrentMap(), TILE_WIDTH, TILE_HEIGHT, 0, 0);
}
void EnemyBoltEntity::animate(float delay)
{
if (boltType != ShotTypeBomb)
particleGenerator.GenerateParticles(frame, IMAGE_BOLT, x, y, BOLT_WIDTH, BOLT_HEIGHT,
boltType == ShotTypeLightning ? Vector2D(20.0f) : Vector2D(0.0f, 0.0f),
10, renderScale);
z = y + height;
CollidingSpriteEntity::animate(delay);
if (boltType != ShotTypeBomb && (lifetime - age) < 0.2f)
{
if (age >= lifetime)
sprite.setColor(sf::Color(255, 255, 255, 0));
else
sprite.setColor(sf::Color(255, 255, 255, (sf::Uint8)((lifetime - age) / 0.2f * 255)));
}
if (boltType == ShotTypeIllusion)
{
float limit = 16;
if (x < limit
|| x > TILE_WIDTH * MAP_WIDTH - limit
|| y < limit
|| y > TILE_HEIGHT * MAP_HEIGHT - limit)
{
isDying = true;
}
}
}
+
+void EnemyBoltEntity::onDying()
+{
+ if (!isDying)
+ {
+ isDying = true;
+ // TO DO effect zone ?
+ }
+}
diff --git a/src/EnemyBoltEntity.h b/src/EnemyBoltEntity.h
index b5430a8..e900f0d 100644
--- a/src/EnemyBoltEntity.h
+++ b/src/EnemyBoltEntity.h
@@ -1,14 +1,17 @@
#ifndef ENNEMYBOLTENTITY_H
#define ENNEMYBOLTENTITY_H
#include "BoltEntity.h"
class EnemyBoltEntity : public BoltEntity
{
public:
EnemyBoltEntity(float x, float y, enumShotType boltType, int level, enemyTypeEnum enemyType);
virtual void animate(float delay);
+
+ protected:
+ virtual void onDying();
};
#endif // ENNEMYBOLTENTITY_H
diff --git a/src/EnemyEntity.cpp b/src/EnemyEntity.cpp
index 6159e69..76a613b 100644
--- a/src/EnemyEntity.cpp
+++ b/src/EnemyEntity.cpp
@@ -1,432 +1,432 @@
#include "EnemyEntity.h"
#include "PlayerEntity.h"
#include "ExplosionEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
-const float FACING_DELAY = 0.25f;
+const float FACING_DELAY = 0.17f;
EnemyEntity::EnemyEntity(sf::Texture* image, float x, float y)
: BaseCreatureEntity (image, x, y, 64, 64)
{
type = ENTITY_ENEMY;
bloodColor = BloodRed;
z = y;
h = 0;
age = -0.001f * (rand()%800) - 0.4f;
deathFrame = -1;
dyingFrame = -1;
dyingSound = SOUND_ENNEMY_DYING;
agonizingSound = SOUND_NONE;
hurtingSound = SOUND_NONE;
isAgonising = false;
enemyType = NB_ENEMY;
meleeLevel = 0;
meleeType = ShotTypeStandard;
canExplode = true;
label_dy = 0;
nextFacingDirection = 0;
facingTimer = -1.0f;
}
enemyTypeEnum EnemyEntity::getEnemyType()
{
return enemyType;
}
void EnemyEntity::checkNextFacing(float dt)
{
if (facingTimer > 0.0f) facingTimer -= dt;
if (facingDirection != nextFacingDirection && facingTimer <= 0.0f)
{
facingDirection = nextFacingDirection;
facingTimer = FACING_DELAY;
}
}
void EnemyEntity::setLabelDy(float label_dy)
{
this->label_dy = label_dy;
}
void EnemyEntity::animate(float delay)
{
if (isAgonising)
{
if (hpDisplay > hp) hpDisplay--;
if (h < -0.01f)
{
isAgonising = false;
isDying = true;
game().addCorpse(x, y, deathFrame);
if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
if (canCollide()) testSpriteCollisions();
if (age > 0.0f)
BaseCreatureEntity::animate(delay);
else
age += delay;
// FIX enemy not on map
if (x < TILE_WIDTH / 2 || x > MAP_WIDTH * TILE_WIDTH || y < TILE_HEIGHT / 2 || y > MAP_HEIGHT * TILE_HEIGHT)
isDying = true;
}
void EnemyEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2;
boundingBox.width = width;
boundingBox.top = (int)y - height / 2;
boundingBox.height = height;
}
void EnemyEntity::collideMapRight()
{
velocity.x = 0.0f;
}
void EnemyEntity::collideMapLeft()
{
velocity.x = 0.0f;
}
void EnemyEntity::collideMapTop()
{
velocity.y = 0.0f;
}
void EnemyEntity::collideMapBottom()
{
velocity.y = 0.0f;
}
void EnemyEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead())
{
if (playerEntity->hurt(getHurtParams(meleeDamages, meleeType, meleeLevel, false, SourceTypeBolt, enemyType, false)) > 0)
{
float xs = (x + playerEntity->getX()) / 2;
float ys = (y + playerEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
if (playerEntity->isEquiped(EQUIP_ROBE_ADVANCED))
{
- giveRecoil(true, Vector2D(playerEntity->getX(), playerEntity->getY()).vectorTo(Vector2D(x, y), 800), 0.8f);
+ giveRepulsion(true, Vector2D(playerEntity->getX(), playerEntity->getY()).vectorTo(Vector2D(x, y), 800), 0.8f);
SoundManager::getInstance().playSound(SOUND_ELECTRIC_CHARGE);
star->setScale(1.5f, 1.5f);
star->setColor(sf::Color(220, 180, 255));
star->setLifetime(1.3f);
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.2f);
}
}
- inflictsRecoilTo(playerEntity);
+ inflictsRepulsionTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
collideWithBolt(boltEntity);
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
void EnemyEntity::collideWithBolt(BoltEntity* boltEntity)
{
float xs = (x + boltEntity->getX()) / 2;
float ys = (y + boltEntity->getY()) / 2;
int maxDamages = hp;
int boltDamages = hurt(getHurtParams
(boltEntity->getDamages(),
boltEntity->getBoltType(),
boltEntity->getLevel(),
boltEntity->isCritical(),
SourceTypeBolt,
enemyType, boltEntity->getGoThrough()));
if (hp > 0)
{
boltEntity->loseDamages(boltEntity->getDamages());
}
else
{
boltEntity->loseDamages(maxDamages >= boltDamages ? boltDamages : maxDamages);
}
if (bloodColor > BloodNone) game().generateBlood(x, y, bloodColor);
SoundManager::getInstance().playSound(SOUND_IMPACT);
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
if (boltEntity->getBoltType() == ShotTypeStone)
{
float factor = (boltEntity->isFromPlayer() && game().getPlayer()->isEquiped(EQUIP_RAPID_SHOT)) ? 0.25f : 1.0f;
float recoilVelocity = factor * STONE_DECOIL_VELOCITY[boltEntity->getLevel()];
float recoilDelay = factor * STONE_DECOIL_DELAY[boltEntity->getLevel()];
Vector2D recoilVector = Vector2D(0, 0).vectorTo(boltEntity->getVelocity(),
recoilVelocity );
- giveRecoil(true, recoilVector, recoilDelay);
+ giveRepulsion(true, recoilVector, recoilDelay);
}
boltEntity->collide();
}
int EnemyEntity::getCollisionDirection(BoltEntity* boltEntity)
{
int tol = 4;
float bx = boltEntity->getX();
float by = boltEntity->getY();
int alignX, alignY;
if (bx + tol < x) alignX = 4;
else if (bx - tol > x) alignX = 6;
else alignX = 5;
if (by + tol < y) alignY = 8;
else if (by - tol > y) alignY = 2;
else alignY = 5;
int collisionDir = 5;
if (alignX == 4)
{
if (alignY == 8) collisionDir = 7;
else if (alignY == 5) collisionDir = 4;
else if (alignY == 2) collisionDir = 1;
}
else if (alignX == 5)
{
if (alignY == 8) collisionDir = 8;
else if (alignY == 5) collisionDir = 5;
else if (alignY == 2) collisionDir = 2;
}
else if (alignX == 6)
{
if (alignY == 8) collisionDir = 9;
else if (alignY == 5) collisionDir = 6;
else if (alignY == 2) collisionDir = 3;
}
return collisionDir;
}
void EnemyEntity::collideWithEnemy(EnemyEntity* entity)
{
// To implement the behaviour when colliding with another ennemy
}
int EnemyEntity::hurt(StructHurt hurtParam)
{
int hurtedHp = BaseCreatureEntity::hurt(hurtParam);
if (hurtedHp > 0 && hurtingSound != SOUND_NONE && hp > 0)
SoundManager::getInstance().playSound(hurtingSound);
return hurtedHp;
}
void EnemyEntity::dying()
{
if (dyingFrame > -1)
{
isAgonising = true;
hVelocity = 200.0f;
if (agonizingSound != SOUND_NONE) SoundManager::getInstance().playSound(agonizingSound);
}
else // dyingFrame == -1
{
isDying = true;
game().addCorpse(x, y, deathFrame);
if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound);
}
if (bloodColor != BloodNone) for (int i = 0; i < 4; i++) game().generateBlood(x, y, bloodColor);
drop();
game().addKilledEnemy(enemyType, hurtingType);
}
void EnemyEntity::dropItem(enumItemType item)
{
ItemEntity* newItem = new ItemEntity(item, x, y);
newItem->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
newItem->setVelocity(Vector2D(50.0f + rand()% 140));
newItem->setViscosity(0.96f);
newItem->startsJumping();
}
void EnemyEntity::drop()
{
if (rand() % 40 == 0)
{
if (rand() % 2 == 0)
{
dropItem(ItemScrollRevelation);
}
else
{
dropItem((enumItemType)(ItemPotion01 + rand() % NUMBER_UNIDENTIFIED));
}
}
else
{
if (rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
if (game().getPlayer()->isEquiped(EQUIP_LUCK) && rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
if (rand() % 25 == 0)
{
dropItem(ItemHealthVerySmall);
}
}
}
bool EnemyEntity::canCollide()
{
return (!isAgonising);
}
void EnemyEntity::render(sf::RenderTarget* app)
{
if (isAgonising || (isDying && dyingFrame > -1))
{
if (shadowFrame > -1)
{
// shadow
int nx = shadowFrame;
int ny = 0;
if (imagesProLine > 0 && shadowFrame >= imagesProLine)
{
nx = shadowFrame % imagesProLine;
ny = shadowFrame / imagesProLine;
}
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
}
int nx = dyingFrame;
int ny = 0;
if (imagesProLine > 0)
{
nx = dyingFrame % imagesProLine;
ny = dyingFrame / imagesProLine;
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect(nx * width + width, ny * height, -width, height));
else
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
}
else
BaseCreatureEntity::render(app);
}
void EnemyEntity::renderLifeBar(sf::RenderTarget* app, std::string label)
{
game().addLifeBarToDisplay(label, hpDisplay, hpMax);
}
bool EnemyEntity::testEntityInMap()
{
int collideCounter = 6;
while (isCollidingWithMap() && collideCounter > 0)
{
int movCounter = 100;
if (collideWithMap(DIRECTION_LEFT) && !collideWithMap(DIRECTION_RIGHT))
{
x = (float)((int)x);
while (collideWithMap(DIRECTION_LEFT) && movCounter > 0)
{
x--;
movCounter--;
}
}
else if (collideWithMap(DIRECTION_RIGHT) && !collideWithMap(DIRECTION_LEFT))
{
x = (float)((int)x);
while (collideWithMap(DIRECTION_RIGHT) && movCounter > 0)
{
x++;
movCounter--;
}
}
else if (collideWithMap(DIRECTION_BOTTOM) && !collideWithMap(DIRECTION_TOP))
{
y = (float)((int)y);
while (collideWithMap(DIRECTION_BOTTOM) && movCounter > 0)
{
y--;
movCounter--;
}
}
else if (collideWithMap(DIRECTION_TOP) && !collideWithMap(DIRECTION_BOTTOM))
{
y = (float)((int)y);
while (collideWithMap(DIRECTION_TOP) && movCounter > 0)
{
y++;
movCounter--;
}
}
collideCounter--;
}
return (collideCounter > 0);
}
diff --git a/src/ExplosionEntity.cpp b/src/ExplosionEntity.cpp
index 9209cf2..7e29d3c 100644
--- a/src/ExplosionEntity.cpp
+++ b/src/ExplosionEntity.cpp
@@ -1,102 +1,102 @@
#include "ExplosionEntity.h"
#include "BaseCreatureEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "sfml_game/SpriteEntity.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include <iostream>
ExplosionEntity::ExplosionEntity(float x, float y, explosionTypeEnum explosionType, int damage, enemyTypeEnum enemyType, bool canHurtPlayer)
: SpriteEntity(ImageManager::getInstance().getImage(IMAGE_EXPLOSION), x, y, 100, 100)
{
type = ENTITY_EXPLOSION;
this->explosionType = explosionType;
imagesProLine = 6;
lifetime = 0.6f;
this->canHurtPlayer = canHurtPlayer;
this->damage = damage;
switch (explosionType)
{
case ExplosionTypeStandard: frame = 0; break;
case ExplosionTypeViolet: frame = 6; break;
}
sprite.setOrigin(50, 50);
testCollisions();
}
void ExplosionEntity::setCanHurtPlayer(bool can)
{
canHurtPlayer = can;
}
void ExplosionEntity::animate(float delay)
{
if (lifetime > 0)
{
if (age >= lifetime) isDying = true;
}
age += delay;
z = y + height / 2;
frame = age / lifetime * 6;
if (frame > 5) frame = 5;
switch (explosionType)
{
case ExplosionTypeStandard: break;
case ExplosionTypeViolet: frame += 6; break;
}
}
void ExplosionEntity::render(sf::RenderTarget* app)
{
SpriteEntity::render(app);
}
void ExplosionEntity::dying()
{
isDying = true;
}
void ExplosionEntity::testCollisions()
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
BaseCreatureEntity* entity = dynamic_cast<BaseCreatureEntity*>(e);
if (entity != NULL)
{
bool ok = true;
if (!canHurtPlayer && entity->getType() == ENTITY_PLAYER) ok = false;
if (ok && entity->getHp() > 0 && entity->canCollide())
{
entity->calculateBB();
sf::IntRect bb;
bb.left = x - 90;
bb.width = 180;
bb.top = y - 90;
bb.height = 180;
if (bb.intersects(entity->getBoundingBox()))
{
// TODO explosion type
if (damage > 0) entity->hurt(BaseCreatureEntity::getHurtParams(damage, ShotTypeFire, 0, false, SourceTypeExplosion, enemyType, false));
- Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(entity->getX(), entity->getY()), 800.0f );
- entity->giveRecoil(true, recoilVector, 1.0f);
+ Vector2D repulsionVector = Vector2D(x, y).vectorTo(Vector2D(entity->getX(), entity->getY()), 800.0f );
+ entity->giveRepulsion(true, repulsionVector, 1.0f);
}
}
}
}
}
diff --git a/src/FranckyEntity.cpp b/src/FranckyEntity.cpp
index a68989a..cb201e9 100644
--- a/src/FranckyEntity.cpp
+++ b/src/FranckyEntity.cpp
@@ -1,658 +1,658 @@
#include "FranckyEntity.h"
#include "BoltEntity.h"
#include "EnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
FranckyEntity::FranckyEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_FRANCKY), x, y)
{
width = 128;
height = 192;
creatureSpeed = 140;
velocity = Vector2D(creatureSpeed);
hp = 900;
hpDisplay = hp;
hpMax = hp;
meleeDamages = 16;
type = ENTITY_ENEMY_BOSS;
bloodColor = BloodRed;
deathFrame = FRAME_CORPSE_FRANCKY_TORSO;
shadowFrame = 5;
agonizingSound = SOUND_FRANCKY_DYING;
frame = 0;
if (game().getPlayerPosition().x > x) isMirroring = true;
sprite.setOrigin(64.0f, 143.0f);
state = 0;
timer = 3.0f;
followTimer = -1.0f;
counter = 10;
age = -1.5f;
enemyType = EnemyTypeFrancky;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceImmune;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistanceLightning] = ResistanceVeryHigh;
canExplode = false;
}
void FranckyEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
// special states
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
// IA
timer -= delay;
if (timer < 0.0f)
{
state++;
if (state == 5)
{
state = 0;
followTimer = 0.25f;
}
int health;
switch (state)
{
case 0:
timer = 6.0f;
health = ((100 * hp) / hpMax);
if (health <= 33) timer = 4.5f;
else if (health <= 66) timer = 5.25f;
SoundManager::getInstance().playSound(SOUND_FRANCKY_00);
break;
case 1:
timer = 1.0f;
velocity.x = 0.0f;
velocity.y = 0.0f;
SoundManager::getInstance().playSound(SOUND_FRANCKY_01);
break;
case 3: timer = 0.2f; break;
case 2:
case 4: timer = 0.2f; fire(); break;
}
}
if (state == 0)
{
// walking
followTimer -= delay;
if (followTimer <= 0.0f)
{
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
followTimer = 0.25f;
}
}
else
{
SoundManager::getInstance().playSound(SOUND_ELECTRICITY, false);
}
// collisions
if (canCollide()) testSpriteCollisions();
BaseCreatureEntity::animate(delay);
// current frame
if (state == 0)
{
int r = ((int)(age * 5.0f)) % 4;
if (r == 2) frame = 0;
else if (r == 3) frame = 2;
else frame = r;
// frame's mirroring
if (velocity.x > 1.0f)
isMirroring = true;
else if (velocity.x < -1.0f)
isMirroring = false;
}
else
{
frame = 3 +((int)(age * 7.0f)) % 2;
isMirroring = game().getPlayer()->getX() > x;
}
z = y + 36;
}
void FranckyEntity::calculateBB()
{
boundingBox.left = (int)x - 32;
boundingBox.width = 58;
boundingBox.top = (int)y - 52;
boundingBox.height = 90;
}
void FranckyEntity::collideMapRight()
{
velocity.x = -velocity.x;
}
void FranckyEntity::collideMapLeft()
{
velocity.x = -velocity.x;
}
void FranckyEntity::collideMapTop()
{
velocity.y = -velocity.y;
}
void FranckyEntity::collideMapBottom()
{
velocity.y = -velocity.y;
}
void FranckyEntity::drop()
{
dropItem(ItemBossHeart);
}
void FranckyEntity::render(sf::RenderTarget* app)
{
EnemyEntity::render(app);
renderLifeBar(app, tools::getLabel("enemy_francky"));
}
void FranckyEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
- inflictsRecoilTo(entity);
+ inflictsRepulsionTo(entity);
}
-void FranckyEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void FranckyEntity::inflictsRepulsionTo(BaseCreatureEntity* targetEntity)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
- Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 450.0f);
- targetEntity->giveRecoil(true, recoilVector, 0.5f);
+ Vector2D repulsionVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 450.0f);
+ targetEntity->giveRepulsion(true, repulsionVector, 0.5f);
}
}
void FranckyEntity::dying()
{
// generates body parts
new FranckyEntityHand(x + 0.01f * (rand() % 100), y);
new FranckyEntityHand(x, y + 0.01f * (rand() % 100));
new FranckyEntityFoot(x + 0.01f * (rand() % 100), y);
new FranckyEntityFoot(x, y + 0.01f * (rand() % 100));
new FranckyEntityHead(x, y);
// FX
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_GIANT_SLIME), x, y, 128, 128, 8);
star->setFrame(4);
star->setColor(sf::Color(230, 255, 255));
star->setFading(true);
star->setZ(y+ 100);
star->setAge(-0.4f);
star->setLifetime(0.3f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
SoundManager::getInstance().playSound(SOUND_SLIME_SMASH);
EnemyEntity::dying();
}
void FranckyEntity::fire()
{
SoundManager::getInstance().playSound(SOUND_ELECTRIC_BLAST);
int health = ((100 * hp) / hpMax);
float boltLifetime = 1.4f;
float boltSpeed = 370;
if (health <= 33)
{
boltLifetime = 1.8f;
boltSpeed = 430;
}
else if (health <= 66)
{
boltLifetime = 1.6f;
boltSpeed = 400;
}
for (float i = state == 2 ? 0.0f : PI / 8; i < 2 * PI; i += PI / 4)
{
EnemyBoltEntity* bolt = new EnemyBoltEntity(x, y, ShotTypeLightning, 0, enemyType);
bolt->setDamages(12);
float velx = boltSpeed * cos(i);
float vely = boltSpeed * sin(i);
bolt->setVelocity(Vector2D(velx, vely));
bolt->setLifetime(boltLifetime);
}
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.3f);
}
///////////////////////////////////////////////////////////////////
//// HAND
///////////////////////////////////////////////////////////////////
FranckyEntityHand::FranckyEntityHand(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_FRANCKY), x, y)
{
creatureSpeed = 260;
velocity = Vector2D(creatureSpeed);
hp = 40;
meleeDamages = 8;
imagesProLine = 10;
bloodColor = BloodRed;
shadowFrame = 3 * imagesProLine + 5;
movingStyle = movWalking;
deathFrame = FRAME_CORPSE_FRANCKY_HAND;
sprite.setOrigin(32.0f, 46.0f);
enemyType = EnemyTypeFranckyHand;
frame = 3 * imagesProLine + 1;
age = 0.0f;
}
void FranckyEntityHand::animate(float delay)
{
if (!isAgonising)
{
if (age < 0.0f) frame = 3 * imagesProLine + 3;
else frame = 3 * imagesProLine + 3 + ((int)(age * 6.0f)) % 2;
isMirroring = velocity.x > 1.0f;
}
EnemyEntity::animate(delay);
}
void FranckyEntityHand::calculateBB()
{
boundingBox.left = (int)x - 20;
boundingBox.width = 40;
boundingBox.top = (int)y - 8;
boundingBox.height = 22;
}
void FranckyEntityHand::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void FranckyEntityHand::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void FranckyEntityHand::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void FranckyEntityHand::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void FranckyEntityHand::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getEnemyType() != EnemyTypeSpiderWeb && entity->getMovingStyle() == movWalking)
{
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), creatureSpeed ));
}
}
void FranckyEntityHand::drop()
{
// Nothing
}
///////////////////////////////////////////////////////////////////
//// HEAD
///////////////////////////////////////////////////////////////////
FranckyEntityHead::FranckyEntityHead(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_FRANCKY), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = 150;
hpMax = hp;
hpDisplay = hp;
meleeDamages = 5;
jumpingDelay = 0.0f;
enemyType = EnemyTypeFranckyHead;
bloodColor = BloodRed;
imagesProLine = 10;
frame = 3 * imagesProLine;
shadowFrame = 3 * imagesProLine + 2;
deathFrame = FRAME_CORPSE_FRANCKY_HEAD;
agonizingSound = SOUND_FRANCKY_DYING;
isJumping = false;
h = 0.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
resistance[ResistanceLightning] = ResistanceVeryHigh;
canExplode = false;
age = 0.0f;
}
void FranckyEntityHead::animate(float delay)
{
float slimeDelay = delay;
if (specialState[SpecialStateIce].active) slimeDelay = delay * specialState[SpecialStateIce].param1;
if (isJumping)
{
hVelocity -= 700.0f * slimeDelay;
h += hVelocity * slimeDelay;
bool firstTimeGround = false;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
firstTimeGround = true;
hVelocity = 120.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
fire();
game().generateBlood(x, y, BloodRed);
}
else
{
jumpingDelay = 0.05f;
isJumping = false;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
int r = rand() % 4;
if (r <= 1) SoundManager::getInstance().playSound(SOUND_FRANCKY_01 + r);
fire();
game().generateBlood(x, y, BloodRed);
}
}
}
if (firstTimeGround) frame = 2;
else if (hVelocity > -60.0f) frame = 1;
else frame = 0;
}
else
{
jumpingDelay -= slimeDelay;
if (jumpingDelay < 0.0f)
{
hVelocity = 180.0f;
isJumping = true;
isFirstJumping = true;
float randVel = 280.0f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), randVel ));
}
}
frame = imagesProLine * 3 + (int)(age * 3) % 2;
isMirroring = velocity.x > 1.0f;
EnemyEntity::animate(delay);
z = y + 14;
}
void FranckyEntityHead::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect((shadowFrame % imagesProLine) * width, (shadowFrame / imagesProLine) * width, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect((frame % imagesProLine) * width + width, (frame / imagesProLine) * width, -width, height));
else
sprite.setTextureRect(sf::IntRect((frame % imagesProLine) * width, (frame / imagesProLine) * width, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
renderLifeBar(app, tools::getLabel("enemy_francky_head"));
}
void FranckyEntityHead::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 14;
boundingBox.height = 28;
}
void FranckyEntityHead::collideMapRight()
{
velocity.x = -velocity.x * 0.8f;
}
void FranckyEntityHead::collideMapLeft()
{
velocity.x = -velocity.x * 0.8f;
}
void FranckyEntityHead::collideMapTop()
{
velocity.y = -velocity.y * 0.8f;
}
void FranckyEntityHead::collideMapBottom()
{
velocity.y = -velocity.y * 0.8f;
}
void FranckyEntityHead::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking)
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), velocity.norm() ));
}
void FranckyEntityHead::fire()
{
SoundManager::getInstance().playSound(SOUND_ELECTRIC_BLAST);
EnemyBoltEntity* bolt = new EnemyBoltEntity
(x, y + 10, ShotTypeLightning, 0, enemyType);
bolt->setDamages(8);
bolt->setLifetime(1.5f);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
bolt->setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), 400 ));
}
///////////////////////////////////////////////////////////////////
//// FOOT
///////////////////////////////////////////////////////////////////
FranckyEntityFoot::FranckyEntityFoot(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_FRANCKY), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = 40;
meleeDamages = 5;
jumpingDelay = 0.0f;
enemyType = EnemyTypeFranckyFoot;
bloodColor = BloodRed;
imagesProLine = 10;
frame = 3 * imagesProLine + 6;
shadowFrame = 3 * imagesProLine + 8;
deathFrame = FRAME_CORPSE_FRANCKY_FOOT;
isJumping = false;
h = 0.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
canExplode = false;
age = 0.0f;
}
void FranckyEntityFoot::animate(float delay)
{
float slimeDelay = delay;
if (specialState[SpecialStateIce].active) slimeDelay = delay * specialState[SpecialStateIce].param1;
if (isJumping)
{
hVelocity -= 700.0f * slimeDelay;
h += hVelocity * slimeDelay;
bool firstTimeGround = false;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
firstTimeGround = true;
hVelocity = 120.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
game().generateBlood(x, y, BloodRed);
}
else
{
jumpingDelay = 0.05f;
isJumping = false;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
game().generateBlood(x, y, BloodRed);
}
}
}
if (firstTimeGround) frame = 2;
else if (hVelocity > -60.0f) frame = 1;
else frame = 0;
}
else
{
jumpingDelay -= slimeDelay;
if (jumpingDelay < 0.0f)
{
hVelocity = 180.0f + rand() % 180;
isJumping = true;
isFirstJumping = true;
float randVel = 280.0f + rand() % 250;
setVelocity(Vector2D(randVel ));
}
}
frame = imagesProLine * 3 + 6 + (int)(age * 3) % 2;
isMirroring = velocity.x > 1.0f;
EnemyEntity::animate(delay);
z = y + 14;
}
void FranckyEntityFoot::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect((shadowFrame % imagesProLine) * width, (shadowFrame / imagesProLine) * width, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect((frame % imagesProLine) * width + width, (frame / imagesProLine) * width, -width, height));
else
sprite.setTextureRect(sf::IntRect((frame % imagesProLine) * width, (frame / imagesProLine) * width, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void FranckyEntityFoot::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 14;
boundingBox.height = 28;
}
void FranckyEntityFoot::collideMapRight()
{
velocity.x = -velocity.x * 0.8f;
}
void FranckyEntityFoot::collideMapLeft()
{
velocity.x = -velocity.x * 0.8f;
}
void FranckyEntityFoot::collideMapTop()
{
velocity.y = -velocity.y * 0.8f;
}
void FranckyEntityFoot::collideMapBottom()
{
velocity.y = -velocity.y * 0.8f;
}
void FranckyEntityFoot::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking)
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), velocity.norm() ));
}
diff --git a/src/FranckyEntity.h b/src/FranckyEntity.h
index 393f9b9..57c8269 100644
--- a/src/FranckyEntity.h
+++ b/src/FranckyEntity.h
@@ -1,103 +1,103 @@
#ifndef FRANCKYENTITY_H
#define FRANCKYENTITY_H
#include "EnemyEntity.h"
#include "PlayerEntity.h"
class FranckyEntity : public EnemyEntity
{
public:
FranckyEntity(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void inflictsRepulsionTo(BaseCreatureEntity* targetEntity) override;
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void dying();
virtual void drop();
private:
float timer;
float followTimer;
int state;
int counter;
void fire();
};
///////////////////////////////////////////////////////////////////
class FranckyEntityHand : public EnemyEntity
{
public:
FranckyEntityHand(float x, float y);
virtual void animate(float delay);
virtual void calculateBB();
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void drop();
};
///////////////////////////////////////////////////////////////////
class FranckyEntityHead : public EnemyEntity
{
public:
FranckyEntityHead(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void collideWithEnemy(EnemyEntity* entity) override;
private:
float jumpingDelay;
bool isJumping;
bool isFirstJumping;
void fire();
};
///////////////////////////////////////////////////////////////////
class FranckyEntityFoot : public EnemyEntity
{
public:
FranckyEntityFoot(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void collideWithEnemy(EnemyEntity* entity) override;
private:
float jumpingDelay;
bool isJumping;
bool isFirstJumping;
};
#endif // FRANCKYENTITY_H
diff --git a/src/GameFloor.cpp b/src/GameFloor.cpp
index c9cb722..d6a8632 100644
--- a/src/GameFloor.cpp
+++ b/src/GameFloor.cpp
@@ -1,457 +1,460 @@
#include "GameFloor.h"
#include "Items.h"
#include "WitchBlastGame.h"
#include <time.h>
#include <cstdlib>
#include <stdio.h>
#include <iostream>
GameFloor::GameFloor()
{
this->level = 0;
forceShop = false;
// Init maps
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
maps[i][j] = NULL;
}
GameFloor::GameFloor(int level)
{
forceShop = false;
// Init maps
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
maps[i][j] = NULL;
this->level = level;
}
void GameFloor::setForceShop()
{
forceShop = true;
}
GameFloor::~GameFloor()
{
// Free maps
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
if (maps[i][j] != NULL) delete (maps[i][j]);
}
roomTypeEnum GameFloor::getRoom(int x, int y)
{
if (x < 0 || y < 0 || x >= FLOOR_WIDTH || y >= FLOOR_HEIGHT) return roomTypeNULL;
return floor[x][y];
}
void GameFloor::setRoom(int x, int y, roomTypeEnum roomType)
{
floor[x][y] = roomType;
}
bool GameFloor::hasRoomOfType(roomTypeEnum roomType)
{
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
if (floor[i][j] == roomType) return true;
return false;
}
DungeonMap* GameFloor::getMap(int x, int y)
{
if (x < 0 || y < 0 || x >= FLOOR_WIDTH || y >= FLOOR_HEIGHT) return NULL;
return maps[x][y];
}
void GameFloor::setMap(int x, int y, DungeonMap* map)
{
maps[x][y] = map;
}
DungeonMap* GameFloor::getAndVisitMap(int x, int y)
{
maps[x][y]->setVisited(true);
if (x > 0 && floor[x - 1][y] > 0 && getRoom(x - 1, y) != roomTypeSecret)
maps[x-1][y]->setKnown(true);
if (x < FLOOR_WIDTH - 1 && floor[x + 1][y] > 0 && getRoom(x + 1, y) != roomTypeSecret)
maps[x+1][y]->setKnown(true);;
if (y > 0 && floor[x][y - 1] > 0 && getRoom(x, y - 1) != roomTypeSecret)
maps[x][y-1]->setKnown(true);;
if (y < FLOOR_HEIGHT - 1 && floor[x][y + 1] > 0 && getRoom(x, y + 1) != roomTypeSecret)
maps[x][y+1]->setKnown(true);;
return maps[x][y];
}
void GameFloor::displayToConsole()
{
for (int j=0; j < FLOOR_HEIGHT; j++)
{
for (int i=0; i < FLOOR_WIDTH; i++)
{
switch (floor[i][j])
{
case roomTypeNULL:
printf(".");
break;
case roomTypeStandard:
printf("#");
break;
case roomTypeBoss:
printf("@");
break;
case roomTypeMerchant:
printf("$");
break;
case roomTypeKey:
printf("k");
break;
case roomTypeBonus:
printf("*");
break;
case roomTypeExit:
printf("X");
break;
case roomTypeStarting:
printf("0");
break;
case roomTypeChallenge:
printf("!");
break;
case roomTypeTemple:
printf("+");
break;
case roomTypeSecret:
printf("?");
break;
}
}
printf("\n");
}
}
int GameFloor::neighboorCount(int x, int y)
{
int count = 0;
if (x > 0 && floor[x-1][y] > 0) count++;
if (x < FLOOR_WIDTH - 1 && floor[x+1][y] > 0) count++;
if (y > 0 && floor[x][y-1] > 0) count++;
if (y < FLOOR_HEIGHT - 1 && floor[x][y+1] > 0) count++;
return count;
}
void GameFloor::reveal()
{
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
{
if (floor[i][j] > roomTypeNULL)
{
if (!maps[i][j]->isCleared())
{
maps[i][j]->setKnown(true);
}
}
}
}
void GameFloor::forget(int floorX, int floorY)
{
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
{
if (floor[i][j] > roomTypeNULL)
{
if (!((i == floorX && (j >= floorY - 1 && j <= floorY + 1)) || (j == floorY && (i >= floorX - 1 && i <= floorX + 1))))
maps[i][j]->setKnown(false);
}
}
}
IntCoord GameFloor::getFirstNeighboor(int x, int y)
{
if (x > 0 && floor[x - 1][y] > 0) return IntCoord(x - 1, y);
if (x < FLOOR_WIDTH - 1 && floor[x + 1][y] > 0) return IntCoord(x + 1, y);
if (y > 0 && floor[x][y - 1] > 0) return IntCoord(x, y - 1);
if (y < FLOOR_HEIGHT - 1 && floor[x][y + 1] > 0) return IntCoord(x, y + 1);
return IntCoord(-1, -1);
}
std::vector<IntCoord> GameFloor::findSuperIsolated()
{
std::vector<IntCoord> results;
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
{
if (floor[i][j] == roomTypeStandard && neighboorCount(i, j) == 1)
{
if (isSuperIsolated(i, j))
{
IntCoord result(i, j);
results.push_back(result);
}
}
}
return results;
}
std::vector<IntCoord> GameFloor::findSecretRoom()
{
std::vector<IntCoord> results;
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
{
if (floor[i][j] == roomTypeNULL && neighboorCount(i, j) == 1)
{
IntCoord neighboor = getFirstNeighboor(i, j);
- if (i >= 0 &&
- (floor[neighboor.x][neighboor.y] == roomTypeStandard
- || (floor[neighboor.x][neighboor.y] == roomTypeStarting && neighboor.y != j - 1)))
+
+ bool ok = true;
+
+ if (game().getLevel() > 1 && i == FLOOR_WIDTH / 2 && j == FLOOR_HEIGHT / 2 + 1) ok = false;
+
+ if (ok && (floor[neighboor.x][neighboor.y] == roomTypeStandard
+ || (floor[neighboor.x][neighboor.y] == roomTypeStarting)))
{
IntCoord result(i, j);
results.push_back(result);
}
}
}
if (results.size() > 0) return results;
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
{
if (floor[i][j] == roomTypeNULL && neighboorCount(i, j) == 1)
{
IntCoord neighboor = getFirstNeighboor(i, j);
- if (i >= 0
- && floor[neighboor.x][neighboor.y] != roomTypeMerchant
+ if (floor[neighboor.x][neighboor.y] != roomTypeMerchant
&& floor[neighboor.x][neighboor.y] != roomTypeExit)
{
IntCoord result(i, j);
results.push_back(result);
}
}
}
return results;
}
bool GameFloor::isSuperIsolated(int x, int y)
{
if (neighboorCount(x, y) != 1) return false;
else
{
if (x > 0 && floor[x-1][y]==1 && neighboorCount(x-1, y) == 2) return true;
else if (x < FLOOR_WIDTH - 1 && floor[x+1][y]==1 && neighboorCount(x+1, y) == 2) return true;
else if (y < FLOOR_HEIGHT - 1 && floor[x][y+1]==1 && neighboorCount(x, y+1) == 2) return true;
}
return false;
}
bool GameFloor::finalize()
{
// step 1 : Exit and boss rooms
std::vector<IntCoord> superIsolatedVector = findSuperIsolated();
if (superIsolatedVector.size() == 0)
return false;
else
{
int index = rand() % superIsolatedVector.size();
floor[superIsolatedVector[index].x][superIsolatedVector[index].y] = roomTypeExit;
IntCoord bossCoord = getFirstNeighboor(superIsolatedVector[index].x, superIsolatedVector[index].y);
if (bossCoord.x == -1) return false;
floor[bossCoord.x][bossCoord.y] = roomTypeBoss;
}
// step 2 : bonus, key, etc...
std::vector<IntCoord> isolatedVector;
for (int i=0; i < FLOOR_WIDTH; i++)
for (int j=0; j < FLOOR_HEIGHT; j++)
if (floor[i][j] == 1 && neighboorCount(i, j) == roomTypeStandard)
{
IntCoord found(i, j);
isolatedVector.push_back(found);
}
int nbIsolatedRooms = isolatedVector.size();
if (nbIsolatedRooms < 2) return false;
int index;
// bonus
index = rand() % isolatedVector.size();
floor[isolatedVector[index].x][isolatedVector[index].y] = roomTypeBonus;
isolatedVector.erase(isolatedVector.begin() + index);
// key
index = rand() % isolatedVector.size();
floor[isolatedVector[index].x][isolatedVector[index].y] = roomTypeKey;
isolatedVector.erase(isolatedVector.begin() + index);
int nbIsolatedRoomsMin = 5;
if (level == 1) nbIsolatedRoomsMin = 2;
else if (level == 2) nbIsolatedRoomsMin = 4;
if (nbIsolatedRooms < nbIsolatedRoomsMin) return false;
if (nbIsolatedRooms < 3) return true;
// shop
index = rand() % isolatedVector.size();
floor[isolatedVector[index].x][isolatedVector[index].y] = roomTypeMerchant;
isolatedVector.erase(isolatedVector.begin() + index);
if (level == 1 || nbIsolatedRooms < 4) return true;
// temple
index = rand() % isolatedVector.size();
floor[isolatedVector[index].x][isolatedVector[index].y] = roomTypeTemple;
isolatedVector.erase(isolatedVector.begin() + index);
// challenge
if (isolatedVector.size() > 0)
{
index = rand() % isolatedVector.size();
floor[isolatedVector[index].x][isolatedVector[index].y] = roomTypeChallenge;
isolatedVector.erase(isolatedVector.begin() + index);
}
return true;
}
void GameFloor::createFloor()
{
bool ok=false;
while (!ok)
{
int i, j;
// Free maps
for (i=0; i < FLOOR_WIDTH; i++)
for (j=0; j < FLOOR_HEIGHT; j++)
if (maps[i][j] != NULL)
{
delete (maps[i][j]);
maps[i][j] = NULL;
}
generate();
ok = finalize();
if (ok)
{
// secret
std::vector<IntCoord> secretVector = findSecretRoom();
if (secretVector.size() > 0)
{
int index = rand() % secretVector.size();
floor[secretVector[index].x][secretVector[index].y] = roomTypeSecret;
}
}
int x0 = FLOOR_WIDTH / 2;
int y0 = FLOOR_HEIGHT / 2;
// Maps
for (i=0; i < FLOOR_WIDTH; i++)
for (j=0; j < FLOOR_HEIGHT; j++)
if (floor[i][j] > 0)
{
maps[i][j] = new DungeonMap(this, i, j);
if (i == x0 && j == y0)
maps[i][j]->setRoomType(roomTypeStarting);
else
maps[i][j]->setRoomType((roomTypeEnum)(floor[i][j]));
}
//displayToConsole();
}
}
void GameFloor::generate()
{
int i, j;
int nbRooms;
int requiredRoms = 6 + level * 2;
if (requiredRoms > 18) requiredRoms = 18;
// Init
for (i=0; i < FLOOR_WIDTH; i++)
for (j=0; j < FLOOR_HEIGHT; j++)
{
floor[i][j] = roomTypeNULL;
}
// First room
int x0 = FLOOR_WIDTH / 2;
int y0 = FLOOR_HEIGHT / 2;
floor[x0][y0] = roomTypeStarting;
nbRooms = 1;
// neighboor
while (nbRooms == 1)
{
if (rand() % 3 == 0)
{
floor[x0-1][y0] = roomTypeStandard;
nbRooms++;
}
if (rand() % 3 == 0)
{
floor[x0+1][y0] = roomTypeStandard;
nbRooms++;
}
if (rand() % 3 == 0)
{
floor[x0][y0-1] = roomTypeStandard;
nbRooms++;
}
if (level == 1)
{
if (rand() % 3 == 0)
{
floor[x0][y0+1] = roomTypeStandard;
nbRooms++;
}
}
}
// others
while (nbRooms < requiredRoms)
for (int k = 0; k < 8; k++)
{
i = rand() % FLOOR_WIDTH;
j = rand() % FLOOR_HEIGHT;
if (floor[i][j] == 0 && ( (level == 1) || (i != x0 || j != y0 + 1) ))
{
int n = neighboorCount(i, j);
switch (n)
{
case 1:
{
floor[i][j] = roomTypeStandard;
nbRooms++;
break;
}
case 2:
{
if (rand()% 5 == 0)
{
floor[i][j] = roomTypeStandard;
nbRooms++;
}
break;
}
case 3:
{
if (rand()% 20 == 0)
{
floor[i][j] = roomTypeStandard;
nbRooms++;
}
break;
}
}
}
}
}
diff --git a/src/GhostEntity.cpp b/src/GhostEntity.cpp
index 86a646d..14815b3 100644
--- a/src/GhostEntity.cpp
+++ b/src/GhostEntity.cpp
@@ -1,160 +1,162 @@
#include "GhostEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
GhostEntity::GhostEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_GHOST), x, y)
{
frame = 0;
dyingFrame = 4;
deathFrame = FRAME_CORPSE_GHOST;
enemyType = EnemyTypeGhost;
hp = GHOST_HP;
creatureSpeed = GHOST_SPEED;
meleeDamages = GHOST_DAMAGE;
movingStyle = movFlying;
velocity = Vector2D(creatureSpeed);
timer = 0.0f;
age -= 0.8f;
bloodColor = BloodNone;
agonizingSound = SOUND_GHOST_DYING;
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceHigh;
+ resistance[ResistanceRepulsion] = ResistanceHigh;
resistance[ResistancePoison] = ResistanceImmune;
}
void GhostEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
timer = timer - delay;
if (timer <= 0.0f)
{
timer = 0.25f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
}
frame = ((int)(age * 3.0f)) % 4;
if (frame == 3) frame = 1;
}
isMirroring = x > game().getPlayerPosition().x;
EnemyEntity::animate(delay);
z = y + 32;
}
void GhostEntity::calculateBB()
{
boundingBox.left = (int)x - 15;
boundingBox.width = 30;
boundingBox.top = (int)y - 15;
boundingBox.height = 30;
}
int GhostEntity::getGhostFade()
{
+ if (game().getPlayer()->seeInvisible()) return 100;
+
float dist1 = 24000.f;
float dist2 = 40000.f;
float dist = Vector2D(x, y).distance2(game().getPlayerPosition());
int fade;
if (dist < dist1) fade = 100;
else if (dist > dist2) fade = 0;
else
{
fade =((dist2 - dist) / (dist2 - dist1)) * 100;
if (fade < 0) fade = 0;
if (fade > 100) fade = 100;
}
if (age <= 0.0f)
{
int ageFade = 40;
if (age > -0.5f) ageFade = 0 - 80.0f * age;
return (fade > ageFade ? fade : ageFade);
}
return fade;
}
void GhostEntity::render(sf::RenderTarget* app)
{
int fade = getGhostFade();
if (fade == 100) SoundManager::getInstance().playSound(SOUND_GHOST);
if (fade == 100 || isAgonising)
{
sf::Color color = sprite.getColor();
color.a = 255;
sprite.setColor(color);
EnemyEntity::render(app);
}
else if (fade > 0)
{
sf::Color color = sprite.getColor();
color.a = fade * 255 / 100;
sprite.setColor(color);
EnemyEntity::render(app);
}
}
void GhostEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void GhostEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void GhostEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void GhostEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void GhostEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getEnemyType() == EnemyTypeGhost)
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 1.5f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
}
}
void GhostEntity::collideWithBolt(BoltEntity* boltEntity)
{
int fade = getGhostFade();
if (fade <= 0) return;
if (boltEntity->getBoltType() == ShotTypeIllusion) fade = 100;
boltEntity->setDamages(boltEntity->getDamages() * fade / 100);
EnemyEntity::collideWithBolt(boltEntity);
}
void GhostEntity::drop()
{
EnemyEntity::drop();
}
diff --git a/src/GiantSlimeEntity.cpp b/src/GiantSlimeEntity.cpp
index ae52c7c..146988b 100644
--- a/src/GiantSlimeEntity.cpp
+++ b/src/GiantSlimeEntity.cpp
@@ -1,469 +1,470 @@
#include "GiantSlimeEntity.h"
#include "BoltEntity.h"
#include "EnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "SlimeEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
GiantSlimeEntity::GiantSlimeEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_GIANT_SLIME), x, y)
{
width = 128;
height = 128;
creatureSpeed = GIANT_SLIME_SPEED;
velocity = Vector2D(creatureSpeed);
hp = GIANT_SLIME_HP;
hpDisplay = hp;
hpMax = GIANT_SLIME_HP;
meleeDamages = GIANT_SLIME_DAMAGES;
missileDelay = GIANT_SLIME_MISSILE_DELAY;
type = ENTITY_ENEMY_BOSS;
enemyType = EnemyTypeSlimeBoss;
bloodColor = BloodGreen;
shadowFrame = 3;
frame = 0;
sprite.setOrigin(64.0f, 64.0f);
h = 0.0f;
age = -2.0f;
changeToState(0);
slimeCounter = 0;
slimeTimer =5.0f;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceVeryHigh;
sprite.setOrigin(64, 84);
canExplode = false;
}
void GiantSlimeEntity::changeToState(int n)
{
if (n == 0) // walking
{
state = 0;
counter = 8 + rand() % 7;
timer = -1.0f;
viscosity = 1.0f;
}
else if (n == 1 || n == 3 || n == 5 || n == 8) // waiting
{
state = n;
timer = 1.2f;
setVelocity(Vector2D(0.0f, 0.0f));
}
else if (n == 2) // jumping
{
state = 2;
timer = 4.0f;
viscosity = 0.991f;
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
hVelocity = 420.0f + rand() % 380;
isFirstJumping = true;
float randVel = 350.0f + rand() % 200;
if (rand() % 2 == 0)
{
float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y);
float angle = atan(tan);
if (game().getPlayer()->getY() > y)
setVelocity(Vector2D(sin(angle) * randVel,
cos(angle) * randVel));
else
setVelocity(Vector2D(-sin(angle) * randVel,
-cos(angle) * randVel));
}
else
velocity = Vector2D(randVel);
}
else if (n == 4) // walking
{
state = 4;
if (hp <= hpMax / 4)
counter = 26;
if (hp <= hpMax / 2)
counter = 18;
else
counter = 12;
timer = GIANT_SLIME_MISSILE_DELAY;
}
else if (n == 6) // jumping
{
state = 6;
timer = 1.2f;
viscosity = 1.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
hVelocity = GIANT_SLIME_ULTRA_JUMP_VELOCITY;
}
else if (n == 7) // falling
{
isFalling = false;
state = 7;
timer = GIANT_SLIME_ULTRA_JUMP_TIMER;
hVelocity = GIANT_SLIME_ULTRA_FALL_VELOCITY;
h = 1500;
}
}
void GiantSlimeEntity::animate(float delay)
{
slimeTimer -= delay;
if (slimeTimer <= 0.0f)
{
slimeTypeEnum slimeType = SlimeTypeStandard;
if (game().isAdvancedLevel())
{
slimeType = (slimeTypeEnum)(slimeType + rand() % 3);
}
switch (slimeCounter)
{
case 0:
new SlimeEntity(TILE_WIDTH * 1.5f, TILE_HEIGHT * 1.5f, slimeType, true);
break;
case 1:
new SlimeEntity(TILE_WIDTH * (MAP_WIDTH - 2) + TILE_WIDTH * 0.5f, TILE_HEIGHT * 1.5f, slimeType, true);
break;
case 2:
new SlimeEntity(TILE_WIDTH * (MAP_WIDTH - 2) + TILE_WIDTH * 0.5f, TILE_HEIGHT * (MAP_HEIGHT - 2) + TILE_HEIGHT * 0.5f, slimeType, true);
break;
case 3:
new SlimeEntity(TILE_WIDTH * 1.5f, TILE_HEIGHT * (MAP_HEIGHT - 2) + TILE_HEIGHT * 0.5f, slimeType, true);
break;
}
slimeTimer = 7.0f;
slimeCounter ++;
if (slimeCounter == 4) slimeCounter = 0;
}
if (age <= 0.0f)
{
age += delay;
return;
}
EnemyEntity::animate(delay);
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0) // walking
{
counter--;
if (counter >= 0)
{
timer = 0.5f;
if (hp <= hpMax / 4)
creatureSpeed = GIANT_SLIME_SPEED * 1.4f;
if (hp <= hpMax / 2)
creatureSpeed = GIANT_SLIME_SPEED * 1.2f;
else
creatureSpeed = GIANT_SLIME_SPEED;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), GIANT_SLIME_SPEED ));
}
else
{
int r = rand() % 3;
if (r == 0) changeToState(1);
else if (r == 1) changeToState(3);
else changeToState(5);
}
}
else if (state == 1) // waiting for jumping
{
changeToState(2);
}
else if (state == 2) // jumping
{
changeToState(8);
}
else if (state == 3)
{
changeToState(4);
}
else if (state == 4) // walking
{
counter--;
if (counter >= 0)
{
if (hp <= hpMax / 4)
timer = missileDelay * 0.6f;
if (hp <= hpMax / 2)
timer = missileDelay * 0.8f;
else
timer = missileDelay;
fire();
}
else
{
changeToState(8);
}
}
else if (state == 5)
{
changeToState(6);
}
else if (state == 6) // jump
{
changeToState(7); // fall
}
else if (state == 7) // jump
{
}
else if (state == 8) // jump
{
changeToState(0); // fall
}
}
if (state == 0) // walking
{
frame = ((int)(age * 2.0f)) % 2;
}
else if (state == 1 || state == 5) // waiting to jump
{
if (timer < 0.25f)
frame = 1;
else
frame = 0;
}
else if (state == 2) // jumping
{
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
hVelocity = 160.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
}
else
{
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
viscosity = 0.96f;
changeToState(0);
}
}
}
if (hVelocity > 0.0f) frame = 2;
else frame = 0;
}
else if (state == 6) // ultra jump
{
if (h < 2000)
h += hVelocity * delay;
}
else if (state == 7) // ultra jump
{
if (!isFalling && timer <= 2.2f)
{
isFalling = true;
x = game().getPlayer()->getX();
y = game().getPlayer()->getY();
// to prevent collisions
float x0 = TILE_WIDTH + 1;
float xf = TILE_WIDTH * (MAP_WIDTH - 1) - 1;
float y0 = TILE_HEIGHT + 1;
float yf = TILE_HEIGHT * (MAP_HEIGHT - 1) - 1;
calculateBB();
if (boundingBox.left < x0) x += (x0 - boundingBox.left);
else if (boundingBox.left + boundingBox.width > xf) x -= (boundingBox.left + boundingBox.width - xf);
if (boundingBox.top < y0) y += (y0 - boundingBox.top);
else if (boundingBox.top + boundingBox.height > yf) y -= (boundingBox.top + boundingBox.height - yf);
}
if (timer < 2.3f)
{
h += hVelocity * delay;
if (h <= 0)
{
h = 0;
changeToState(8);
game().makeShake(0.8f);
SoundManager::getInstance().playSound(SOUND_CYCLOPS_IMPACT);
}
}
}
if (state == 6 && timer < 0.5f)
{
int fade = timer * 512;
if (fade < 0) fade = 0;
sprite.setColor(sf::Color(255, 255, 255, fade));
}
else if (state == 7 && timer < 1.5f)
sprite.setColor(sf::Color(255, 255, 255, 255));
else if (state == 7 && timer < 2.0f)
sprite.setColor(sf::Color(255, 255, 255, (2.0f - timer) * 512));
else if (state == 7)
sprite.setColor(sf::Color(255, 255, 255, 0));
isMirroring = (frame == 2) && (velocity.x < 0.0f);
z = y + 26;
}
void GiantSlimeEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + GIANT_SLIME_BB_LEFT;
boundingBox.width = width - GIANT_SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + 40;
boundingBox.height = height - 76;
}
void GiantSlimeEntity::collideMapRight()
{
velocity.x = -velocity.x;
}
void GiantSlimeEntity::collideMapLeft()
{
velocity.x = -velocity.x;
}
void GiantSlimeEntity::collideMapTop()
{
velocity.y = -velocity.y;
}
void GiantSlimeEntity::collideMapBottom()
{
velocity.y = -velocity.y;
}
void GiantSlimeEntity::dying()
{
isDying = true;
game().addKilledEnemy(enemyType, hurtingType);
game().addCorpse(x, y, FRAME_CORPSE_GIANT_SLIME);
float xSlime = x;
float ySlime = y;
if (x <= 1.5 * TILE_WIDTH) x = 1.5f * TILE_WIDTH + 2;
else if (x >= TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH) x = TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH -3;
if (y <= 1.5 * TILE_HEIGHT) y = 1.5 * TILE_HEIGHT + 2;
else if (y >= TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT) y = TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT -3;
for (int i = 0; i < 9; i++)
{
game().generateBlood(xSlime, ySlime, bloodColor);
- new SlimeEntity(x, y, SlimeTypeStandard, true);
+ SlimeEntity* slime = new SlimeEntity(x, y, SlimeTypeStandard, true);
+ slime->disableCollidingTemporary();
}
game().makeShake(1.0f);
SoundManager::getInstance().playSound(SOUND_SLIME_SMASH);
ItemEntity* newItem = new ItemEntity(ItemBossHeart, x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_GIANT_SLIME), x, y, 128, 128, 8);
star->setFrame(4);
star->setFading(true);
star->setZ(y+ 100);
star->setAge(-0.4f);
star->setLifetime(0.3f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
void GiantSlimeEntity::render(sf::RenderTarget* app)
{
if (!isDying)
{
// shadow
sprite.setPosition(x, y);
if (isMirroring)
sprite.setTextureRect(sf::IntRect(shadowFrame * width + width, 0, -width, height));
else
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect(frame * width + width, 0, -width, height));
else
sprite.setTextureRect(sf::IntRect(frame * width, 0, width, height));
app->draw(sprite);
renderLifeBar(app, tools::getLabel("enemy_giant_slime"));
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void GiantSlimeEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
inflictsRecoilTo(entity);
}
}
void GiantSlimeEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
if (state == 7)
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), KING_RAT_RUNNING_RECOIL );
- targetEntity->giveRecoil(true, recoilVector, 1.0f);
+ targetEntity->giveRepulsion(true, recoilVector, 1.0f);
}
}
bool GiantSlimeEntity::canCollide()
{
return h <= 70.0f;
}
BaseCreatureEntity::enumMovingStyle GiantSlimeEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
void GiantSlimeEntity::fire()
{
SoundManager::getInstance().playSound(SOUND_BLAST_FLOWER);
EnemyBoltEntity* bolt = new EnemyBoltEntity
(x, y + 10, ShotTypeStandard, 0, enemyType);
bolt->setFrame(1);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
bolt->setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(),GIANT_SLIME_FIRE_VELOCITY ));
}
diff --git a/src/GiantSpiderEntity.cpp b/src/GiantSpiderEntity.cpp
index 42e45da..104800e 100644
--- a/src/GiantSpiderEntity.cpp
+++ b/src/GiantSpiderEntity.cpp
@@ -1,350 +1,350 @@
#include "GiantSpiderEntity.h"
#include "EnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "SpiderEggEntity.h"
#include "SpiderWebEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
GiantSpiderEntity::GiantSpiderEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_GIANT_SPIDER), x, y)
{
width = 128;
height = 128;
creatureSpeed = GIANT_SPIDER_SPEED[0];
velocity = Vector2D(0.0f, 0.0f);
hp = GIANT_SPIDER_HP;
hpMax = hp;
hpDisplay = hp;
meleeDamages = GIANT_SPIDER_DAMAGE;
type = ENTITY_ENEMY;
enemyType = EnemyTypeSpiderGiant;
bloodColor = BloodGreen;
shadowFrame = 3;
dyingFrame = 4;
deathFrame = FRAME_CORPSE_GIANT_SPIDER;
agonizingSound = SOUND_SPIDER_DIE;
sprite.setOrigin(64.0f, 64.0f);
walkingSoundDelay = -1.0f;
h = 2000;
state = 0;
hurtLevel = 0;
labelHasChanged = false;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceImmune;
creatureName = "???";
canExplode = false;
}
void GiantSpiderEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
if (!isAgonising)
{
if (state == 0) // falling
{
h -= delay * 300.0f;
if (h <= 0.0f)
{
h = 0;
state = 1;
timer = 1.0f;
if (!labelHasChanged)
{
labelHasChanged = true;
creatureName = tools::getLabel("enemy_giant_spider");
game().testAndAddMessageToQueue(MsgInfoGiantSpiderAfter);
}
}
}
else if (state == 1) // wait after falling
{
timer -= delay;
if (timer <= 0.0f)
{
state = 2;
velocity = Vector2D(creatureSpeed);
timer = 10.0f;
fireDelay = 0.5f;
}
}
else if (state == 2) // moving
{
walkingSoundDelay -= delay;
if (walkingSoundDelay <= 0.0f)
{
walkingSoundDelay = 1.0f;
SoundManager::getInstance().playSound(SOUND_SPIDER_WALKING);
}
fireDelay -= delay;
if (fireDelay <= 0.0f)
{
if (rand() % 12 == 0)
{
SoundManager::getInstance().playSound(SOUND_SPIDER_WEB);
for (int i = 0; i < 3; i++) new SpiderWebEntity(x, y, false);
}
else
{
for (int i = 0; i < 4; i++) fire(i == 0 ? 1 : 0);
}
fireDelay = GIANT_SPIDER_FIRE_DELAY[hurtLevel];
}
timer -= delay;
if (getHealthLevel() > hurtLevel)
{
hurtLevel++;
state = 3;
velocity = Vector2D(0.0f, 0.0f);
timer = 1.0f;
creatureSpeed = GIANT_SPIDER_SPEED[hurtLevel];
SoundManager::getInstance().playSound(SOUND_SPIDER_HURT);
}
}
else if (state == 3) // wait after falling
{
timer -= delay;
if (timer <= 0.0f) state = 4;
}
else if (state == 4) // moving up
{
h += delay * 300.0f;
if (h >= 1500.0f)
{
state = 5;
timer = 6.0f;
for (int i = 0; i < GIANT_SPIDER_NUMBER_EGGS[hurtLevel]; i++)
{
new SpiderEggEntity(TILE_WIDTH * 1.5f + rand() % (TILE_WIDTH * 12),
TILE_HEIGHT * 1.5f + rand() % (TILE_HEIGHT * 6), true);
}
}
}
else if (state == 5) // waiting to fall
{
timer -= delay;
if (timer <= 0.0f)
state = 0;
}
// frame
frame = 0;
if (state == 2)
{
frame = ((int)(age * 6.0f)) % 4;
if (frame == 2) frame = 0;
else if (frame == 3) frame = 2;
}
}
EnemyEntity::animate(delay);
z = y + 40;
}
int GiantSpiderEntity::hurt(StructHurt hurtParam)
{
if (hurtLevel < getHealthLevel()) hurtParam.damage /= 5;
return EnemyEntity::hurt(hurtParam);
}
void GiantSpiderEntity::calculateBB()
{
boundingBox.left = (int)x - 45;
boundingBox.width = 90;
boundingBox.top = (int)y - 25;
boundingBox.height = 65;
}
void GiantSpiderEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void GiantSpiderEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
}
void GiantSpiderEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void GiantSpiderEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
}
void GiantSpiderEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
Vector2D v = Vector2D(x, y).vectorTo(Vector2D(entity->getX(), entity->getY()), 150.0f);
- entity->giveRecoil(false, v, 0.5f);
+ entity->giveRepulsion(false, v, 0.5f);
}
}
void GiantSpiderEntity::calculateRotation()
{
float h0 = 100;
float hf = 18;
if (state == 0)
{
if (h > h0) sprite.setRotation(180);
else if (h < hf) sprite.setRotation(0);
else
{
sprite.setRotation(180 + (h0 - h) * 180 / (h0 - hf));
}
}
if (state == 4)
{
if (h > h0) sprite.setRotation(180);
else if (h < hf) sprite.setRotation(0);
else
{
sprite.setRotation(180 - (h0 - h) * 180 / (h0 - hf));
}
}
else if (state == 2) sprite.setRotation(0);
}
void GiantSpiderEntity::render(sf::RenderTarget* app)
{
if (!isDying)
{
// shadow
sprite.setRotation(0.0f);
if (state == 0 || state == 4 || state == 5)
{
int h0 = 850;
int hf = 600;
int fade = 0;
if (h < h0)
{
if (h < hf) fade = 255;
else
{
fade = (h0 - h) * 255 / (h0 - hf);
if (fade < 0) fade = 0;
else if (fade > 255) fade = 255;
}
sprite.setColor(sf::Color(255, 255, 255, fade));
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
sprite.setColor(sf::Color(255, 255, 255, 255));
}
}
else
{
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
}
calculateRotation();
if (state == 0 || state == 1 || state == 3 || state == 4)
{
if (y - h > 0)
{
int fade = 255;
if (state == 1) fade = 255 * timer;
else if (state == 3) fade = 255 * (1.0f - timer);
sf::RectangleShape line(sf::Vector2f(2, y - h));
line.setPosition(x - 1, 0);
line.setFillColor(sf::Color(255, 255, 255, fade));
app->draw(line);
}
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, 0, width, height));
app->draw(sprite);
renderLifeBar(app, creatureName);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
bool GiantSpiderEntity::canCollide()
{
return h <= 70.0f;
}
void GiantSpiderEntity::fire(int fireType)
{
SoundManager::getInstance().playSound(SOUND_BLAST_POISON);
EnemyBoltEntity* bolt;
bolt = new EnemyBoltEntity(x, y, ShotTypePoison, 0, enemyType);
bolt->setDamages(5);
float fireVelocity = 180.0f;
if (specialState[SpecialStateIce].active) fireVelocity *= 0.5f;
bolt->setVelocity(Vector2D(fireVelocity));
}
int GiantSpiderEntity::getHealthLevel()
{
int healthLevel = 0;
if (hp <= hpMax * 0.25) healthLevel = 3;
else if (hp <= hpMax * 0.5) healthLevel = 2;
else if (hp <= hpMax * 0.75) healthLevel = 1;
return healthLevel;
}
void GiantSpiderEntity::drop()
{
dropItem(ItemBossHeart);
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
EnemyEntity* entity = dynamic_cast<EnemyEntity*>(e);
if (entity != NULL)
{
if (entity->getEnemyType()== EnemyTypeSpiderWeb)
{
entity->hurt(getHurtParams(entity->getHp(), ShotTypeStandard, 0, false, SourceTypeMelee, enemyType, false));
}
}
}
}
diff --git a/src/GreenRatEntity.cpp b/src/GreenRatEntity.cpp
index c94c93b..e41f69f 100644
--- a/src/GreenRatEntity.cpp
+++ b/src/GreenRatEntity.cpp
@@ -1,141 +1,141 @@
#include "GreenRatEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
GreenRatEntity::GreenRatEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_RAT), x, y)
{
imagesProLine = 10;
creatureSpeed = GREEN_RAT_SPEED;
velocity = Vector2D(creatureSpeed);
computeFacingDirection();
hp = GREEN_RAT_HP;
meleeDamages = GREEN_RAT_DAMAGES;
type = ENTITY_ENEMY_INVOCATED;
enemyType = EnemyTypeRatGreen;
bloodColor = BloodRed;
shadowFrame = -1;
dyingFrame = 19;
deathFrame = FRAME_CORPSE_GREEN_RAT;
agonizingSound = SOUND_RAT_DYING;
timer = (rand() % 50) / 10.0f;
age = -GREEN_RAT_FADE;
frame = 11;
sprite.setOrigin(32.0f, 38.0f);
canExplode = false; // TO SEE
}
void GreenRatEntity::animate(float delay)
{
z = y + boundingBox.top + boundingBox.height;
if (age > 0.0f && !isAgonising)
{
sprite.setColor(sf::Color(255,255,255,255));
timer = timer - delay;
if (timer <= 0.0f)
{
timer = (rand() % 50) / 10.0f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), GREEN_RAT_SPEED ));
computeFacingDirection();
}
frame = 8 + ((int)(age * 5.0f)) % 2;
if (facingDirection == 4 || facingDirection == 6) frame += 2;
isMirroring = (facingDirection == 4 );
if (facingDirection == 8) frame += 4;
frame = ((int)(age * 5.0f)) % 4;
if (frame == 3) frame = 1;
if (facingDirection == 4 || facingDirection == 6) frame += 3;
isMirroring = (facingDirection == 6 );
if (facingDirection == 8) frame += 6;
frame += 10;
}
else if (!isAgonising)
{
sprite.setColor(sf::Color(255,255,255,255 * (1.0 + age)));
}
else if (isAgonising)
{
if (hVelocity < -1.0f)
{
int fade = h * 200 / 25;
if (fade > 200) fade = 200;
else if (fade < 0) fade = 0;
sprite.setColor(sf::Color(255, 255, 255, fade));
}
}
EnemyEntity::animate(delay);
z = y + 17;
}
void GreenRatEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + RAT_BB_LEFT;
boundingBox.width = width - RAT_BB_WIDTH_DIFF;
boundingBox.top = (int)y - 13;
boundingBox.height = 31;
}
void GreenRatEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
computeFacingDirection();
}
void GreenRatEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
computeFacingDirection();
}
void GreenRatEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
computeFacingDirection();
}
void GreenRatEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
computeFacingDirection();
}
bool GreenRatEntity::canCollide()
{
return (age >= 0.0f);
}
void GreenRatEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking )
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 100.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
computeFacingDirection();
}
}
void GreenRatEntity::drop()
{
// no drop
}
diff --git a/src/KingRatEntity.cpp b/src/KingRatEntity.cpp
index 4021691..5b66c7c 100644
--- a/src/KingRatEntity.cpp
+++ b/src/KingRatEntity.cpp
@@ -1,373 +1,373 @@
#include "KingRatEntity.h"
#include "GreenRatEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
KingRatEntity::KingRatEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_KING_RAT), x, y)
{
width = 128;
height = 128;
creatureSpeed = KING_RAT_SPEED;
velocity = Vector2D(creatureSpeed);
hp = KING_RAT_HP;
hpDisplay = hp;
hpMax = KING_RAT_HP;
meleeDamages = KING_RAT_DAMAGES;
imagesProLine = 5;
type = ENTITY_ENEMY_BOSS;
enemyType = EnemyTypeRatKing;
bloodColor = BloodRed;
shadowFrame = 4;
dyingFrame = 5;
deathFrame = FRAME_CORPSE_KING_RAT;
dyingSound = SOUND_KING_RAT_DIE;
frame = 0;
if (game().getPlayerPosition().x > x) sprite.setScale(-1.0f, 1.0f);
sprite.setOrigin(64.0f, 70.0f);
state = 0;
timer = 2.0f + (rand() % 40) / 10.0f;
age = -2.0f;
berserkDelay = 1.0f + rand()%5 * 0.1f;
hasBeenBerserk = false;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceVeryHigh;
canExplode = false;
}
void KingRatEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
if (isAgonising)
{
if (h < -0.01f)
{
isDying = true;
game().addCorpse(x, y, deathFrame);
if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
EnemyEntity::animate(delay);
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
float timerMult = 1.0f;
if (hp <= hpMax / 4)
{
creatureSpeed = KING_RAT_SPEED * 1.4f;
timerMult = 0.7f;
}
else if (hp <= hpMax / 2)
{
creatureSpeed = KING_RAT_SPEED * 1.2f;
timerMult = 0.85f;
}
else
{
creatureSpeed = KING_RAT_SPEED;
}
if (state == 5)
{
velocity.x *= 0.965f;
velocity.y *= 0.965f;
}
else
{
if (velocity.x > 0.1f) sprite.setScale(-1.0f, 1.0f);
else if (velocity.x < -0.1f) sprite.setScale(1.0f, 1.0f);
}
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0)
{
state = 1;
// generate rats
generateGreenRats();
SoundManager::getInstance().playSound(SOUND_KING_RAT_1);
timer = 2.0f;
velocity.x = 0.0f;
velocity.y = 0.0f;
}
else if (state == 1) // generate rats
{
// to normal or berserk
if (hp < hpMax / 4 && !hasBeenBerserk)
{
hasBeenBerserk = true;
timer = 12.0f;
state = 6;
velocity = Vector2D(KING_RAT_BERSERK_SPEED);
SoundManager::getInstance().playSound(SOUND_KING_RAT_2);
}
else
{
timer = 7.0f * timerMult;
velocity = Vector2D(creatureSpeed);
state = 2;
}
}
else if (state == 2) // normal -> rush
{
state = 3;
// angry
timer = 1.0f;
velocity.x = 0.0f;
velocity.y = 0.0f;
SoundManager::getInstance().playSound(SOUND_KING_RAT_2);
}
else if (state == 3) // normal -> rush
{
state = 4;
// rush
timer = 12.0f;
float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y);
float angle = atan(tan);
if (game().getPlayer()->getY() > y)
setVelocity(Vector2D(sin(angle) * KING_RAT_RUNNING_SPEED,
cos(angle) * KING_RAT_RUNNING_SPEED));
else
setVelocity(Vector2D(-sin(angle) * KING_RAT_RUNNING_SPEED,
-cos(angle) * KING_RAT_RUNNING_SPEED));
}
else if (state == 5) // wall impact
{
// to normal or berserk
if (hp < hpMax / 4 && !hasBeenBerserk)
{
hasBeenBerserk = true;
timer = 12.0f;
state = 6;
velocity = Vector2D(KING_RAT_BERSERK_SPEED);
SoundManager::getInstance().playSound(SOUND_KING_RAT_2);
}
else
{
timer = 7.0f * timerMult;
velocity = Vector2D(creatureSpeed);
state = 0;
}
}
else if (state == 6)
{
timer = 7.0f * timerMult;
velocity = Vector2D(creatureSpeed);
state = 0;
}
}
if (state == 6)
{
berserkDelay -= delay;
if (berserkDelay <= 0.0f)
{
berserkDelay = 0.6f + (rand()%10) / 20.0f;
SoundManager::getInstance().playSound(SOUND_KING_RAT_2);
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(),KING_RAT_BERSERK_SPEED ));
}
}
frame = 0;
if (state == 1)
frame = 3;
else if (state == 3 || state == 6)
{
frame = 3; //0;
int r = ((int)(age * 10.0f)) % 2;
if (r == 0)
sprite.setScale(-1.0f, 1.0f);
else
sprite.setScale(1.0f, 1.0f);
}
else if (state == 4)
{
int r = ((int)(age * 7.5f)) % 4;
if (r == 1) frame = 1;
else if (r == 3) frame = 2;
}
else if (state == 5)
{
frame = 0;
}
else
{
int r = ((int)(age * 5.0f)) % 4;
if (r == 1) frame = 1;
else if (r == 3) frame = 2;
}
z = y + 48;
}
int KingRatEntity::hurt(StructHurt hurtParam)
{
// berserk state protects
if (state == 6)
hurtParam.damage /= 4;
return EnemyEntity::hurt(hurtParam);
}
void KingRatEntity::calculateBB()
{
boundingBox.left = (int)x - 35;
boundingBox.width = 70;
boundingBox.top = (int)y - 15;
boundingBox.height = 63;
}
void KingRatEntity::afterWallCollide()
{
if (state == 4)
{
state = 5;
timer = 1.4f;
velocity.x *= 0.75f;
velocity.y *= 0.75f;
SoundManager::getInstance().playSound(SOUND_BIG_WALL_IMPACT);
game().makeShake(0.75f);
}
}
void KingRatEntity::collideMapRight()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void KingRatEntity::collideMapLeft()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void KingRatEntity::collideMapTop()
{
velocity.y = -velocity.y;
afterWallCollide();
}
void KingRatEntity::collideMapBottom()
{
velocity.y = -velocity.y;
afterWallCollide();
}
void KingRatEntity::drop()
{
dropItem(ItemBossHeart);
}
void KingRatEntity::generateGreenRats()
{
for (int i = 0; i < 5; i++)
{
float xr = x + -100 + rand() % 200;
float yr = y + -100 + rand() % 200;
if (xr > TILE_WIDTH * 1.5f && xr < TILE_WIDTH * (MAP_WIDTH - 2)
&& yr > TILE_HEIGHT * 1.5f && yr < TILE_HEIGHT * (MAP_HEIGHT - 2))
{
new GreenRatEntity(xr, yr);
}
else
i--;
}
}
void KingRatEntity::render(sf::RenderTarget* app)
{
EnemyEntity::render(app);
if (state == 6)
{
int r = ((int)(age *12.0f)) % 2;
if (r == 0)
sprite.setTextureRect(sf::IntRect(1 * width, 1 * height, width, height));
else
sprite.setTextureRect(sf::IntRect(2 * width, 1 * height, -width, height));
sprite.setPosition(x, y);
sprite.setColor(sf::Color(255, 255, 255, 190));
app->draw(sprite);
sprite.setColor(sf::Color(255, 255, 255, 255));
}
renderLifeBar(app, tools::getLabel("enemy_rat_king"));
}
void KingRatEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
- inflictsRecoilTo(entity);
+ inflictsrepulsionTo(entity);
}
}
-void KingRatEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void KingRatEntity::inflictsrepulsionTo(BaseCreatureEntity* targetEntity)
{
if (state == 4 || state == 6)
{
- Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), KING_RAT_RUNNING_RECOIL );
- targetEntity->giveRecoil(true, recoilVector, 1.0f);
+ Vector2D repulsionVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), KING_RAT_RUNNING_RECOIL );
+ targetEntity->giveRepulsion(true, repulsionVector, 1.0f);
}
if (state == 6)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
- Vector2D recoilVector = Vector2D(targetEntity->getX(), targetEntity->getY()).vectorTo(Vector2D(x, y), KING_RAT_RUNNING_RECOIL);
- giveRecoil(true, recoilVector, 1.0f);
+ Vector2D repulsionVector = Vector2D(targetEntity->getX(), targetEntity->getY()).vectorTo(Vector2D(x, y), KING_RAT_RUNNING_RECOIL);
+ giveRepulsion(true, repulsionVector, 1.0f);
}
}
}
diff --git a/src/KingRatEntity.h b/src/KingRatEntity.h
index 8c3d29c..ed7f2dd 100644
--- a/src/KingRatEntity.h
+++ b/src/KingRatEntity.h
@@ -1,34 +1,34 @@
#ifndef KINGRATSPRITE_H
#define KINGRATSPRITE_H
#include "EnemyEntity.h"
#include "PlayerEntity.h"
class KingRatEntity : public EnemyEntity
{
public:
KingRatEntity(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void inflictsrepulsionTo(BaseCreatureEntity* targetEntity);
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
void afterWallCollide();
virtual int hurt(StructHurt hurtParam) override;
void generateGreenRats();
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void drop();
private:
float timer;
float berserkDelay;
int state;
bool hasBeenBerserk;
};
#endif // KINGRATSPRITE_H
diff --git a/src/LargeSlimeEntity.cpp b/src/LargeSlimeEntity.cpp
index dd7f392..b41c017 100644
--- a/src/LargeSlimeEntity.cpp
+++ b/src/LargeSlimeEntity.cpp
@@ -1,413 +1,415 @@
#include "LargeSlimeEntity.h"
#include "BoltEntity.h"
#include "EnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
LargeSlimeEntity::LargeSlimeEntity(float x, float y, slimeTypeEnum slimeType)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_GIANT_SLIME), x, y)
{
width = 128;
height = 128;
creatureSpeed = GIANT_SLIME_SPEED;
velocity = Vector2D(creatureSpeed);
hp = GIANT_SLIME_HP / 2;
hpDisplay = hp;
hpMax = hp;
meleeDamages = GIANT_SLIME_DAMAGES;
missileDelay = GIANT_SLIME_MISSILE_DELAY;
type = ENTITY_ENEMY;
bloodColor = BloodGreen;
shadowFrame = 3;
imagesProLine = 5;
h = 0.0f;
age = -1.0f;
this->slimeType = slimeType;
changeToState(0);
switch (slimeType)
{
case SlimeTypeBlue:
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceHigh;
resistance[ResistanceIce] = ResistanceVeryHigh;
resistance[ResistanceFire] = ResistanceVeryLow;
enemyType = EnemyTypeSlimeBlueLarge;
meleeType = ShotTypeIce;
fireSound = SOUND_BLAST_ICE;
fireType = ShotTypeIce;
fireDamage = 8;
baseFrame = 10;
break;
case SlimeTypeRed:
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceHigh;
resistance[ResistanceIce] = ResistanceVeryLow;
resistance[ResistanceFire] = ResistanceVeryHigh;
enemyType = EnemyTypeSlimeRedLarge;
fireSound = SOUND_BLAST_FIRE;
fireType = ShotTypeFire;
fireDamage = 12;
baseFrame = 5;
break;
case SlimeTypeViolet:
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceHigh;
enemyType = EnemyTypeSlimeLarge;
fireSound = SOUND_BLAST_FLOWER;
fireType = ShotTypeStandard;
fireDamage = 8;
baseFrame = 15;
break;
case SlimeTypeStandard:
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceHigh;
enemyType = EnemyTypeSlimeLarge;
fireSound = SOUND_BLAST_FLOWER;
fireType = ShotTypeStandard;
fireDamage = 8;
baseFrame = 0;
break;
}
frame = baseFrame;
sprite.setOrigin(64, 84);
canExplode = false;
}
void LargeSlimeEntity::changeToState(int n)
{
if (n == 0) // walking
{
state = 0;
counter = 5 + rand() % 7;
timer = -1.0f;
viscosity = 1.0f;
}
else if (n == 1 || n == 3 || n == 5 || n == 8) // waiting
{
state = n;
timer = 1.2f;
setVelocity(Vector2D(0.0f, 0.0f));
}
else if (n == 2) // jumping
{
state = 2;
timer = 4.0f;
viscosity = 0.991f;
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
hVelocity = 420.0f + rand() % 380;
isFirstJumping = true;
float randVel = 350.0f + rand() % 200;
if (rand() % 2 == 0)
{
float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y);
float angle = atan(tan);
if (game().getPlayer()->getY() > y)
setVelocity(Vector2D(sin(angle) * randVel,
cos(angle) * randVel));
else
setVelocity(Vector2D(-sin(angle) * randVel,
-cos(angle) * randVel));
}
else
velocity = Vector2D(randVel);
}
else if (n == 4) // walking
{
state = 4;
if (hp <= hpMax / 4)
counter = 10;
if (hp <= hpMax / 2)
counter = 8;
else
counter = 6;
timer = GIANT_SLIME_MISSILE_DELAY;
}
}
void LargeSlimeEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
EnemyEntity::animate(delay);
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].param1;
timer -= delay;
frame = 0;
if (timer <= 0.0f)
{
if (state == 0) // walking
{
counter--;
if (counter >= 0)
{
timer = 0.5f;
if (hp <= hpMax / 4)
creatureSpeed = GIANT_SLIME_SPEED * 1.4f;
if (hp <= hpMax / 2)
creatureSpeed = GIANT_SLIME_SPEED * 1.2f;
else
creatureSpeed = GIANT_SLIME_SPEED;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), GIANT_SLIME_SPEED ));
}
else
{
int r = rand() % 2;
if (r == 0) changeToState(1);
else changeToState(3);
}
}
else if (state == 1) // waiting for jumping
{
changeToState(2);
}
else if (state == 2) // jumping
{
changeToState(8);
}
else if (state == 3)
{
changeToState(4);
}
else if (state == 4) // walking
{
counter--;
if (counter >= 0)
{
if (hp <= hpMax / 4)
timer = missileDelay * 0.6f;
if (hp <= hpMax / 2)
timer = missileDelay * 0.8f;
else
timer = missileDelay;
fire();
}
else
{
changeToState(8);
}
}
else if (state == 8) // jump
{
changeToState(0); // fall
}
}
if (state == 0) // walking
{
frame = ((int)(age * 2.0f)) % 2;
}
else if (state == 1 || state == 5) // waiting to jump
{
if (timer < 0.25f)
frame = 1;
else
frame = 0;
}
else if (state == 2) // jumping
{
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
hVelocity = 160.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
}
else
{
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
viscosity = 0.96f;
changeToState(0);
}
}
}
if (hVelocity > 0.0f) frame = 2;
else frame = 0;
}
isMirroring = (frame == 2) && (velocity.x < 0.0f);
frame += baseFrame;
z = y + 26;
}
void LargeSlimeEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + GIANT_SLIME_BB_LEFT;
boundingBox.width = width - GIANT_SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + 40;
boundingBox.height = height - 76;
}
void LargeSlimeEntity::collideMapRight()
{
velocity.x = -velocity.x;
}
void LargeSlimeEntity::collideMapLeft()
{
velocity.x = -velocity.x;
}
void LargeSlimeEntity::collideMapTop()
{
velocity.y = -velocity.y;
}
void LargeSlimeEntity::collideMapBottom()
{
velocity.y = -velocity.y;
}
void LargeSlimeEntity::dying()
{
isDying = true;
game().addKilledEnemy(enemyType, hurtingType);
if (slimeType == SlimeTypeBlue) game().addCorpse(x, y, FRAME_CORPSE_GIANT_SLIME_BLUE);
else if (slimeType == SlimeTypeRed) game().addCorpse(x, y, FRAME_CORPSE_GIANT_SLIME_RED);
else if (slimeType == SlimeTypeStandard) game().addCorpse(x, y, FRAME_CORPSE_GIANT_SLIME);
float xSlime = x;
float ySlime = y;
if (x <= 1.5 * TILE_WIDTH) x = 1.5f * TILE_WIDTH + 2;
else if (x >= TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH) x = TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH -3;
if (y <= 1.5 * TILE_HEIGHT) y = 1.5 * TILE_HEIGHT + 2;
else if (y >= TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT) y = TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT -3;
for (int i = 0; i < 9; i++)
{
game().generateBlood(xSlime, ySlime, bloodColor);
if (i % 2 == 0)
{
+ SlimeEntity* slime;
switch (slimeType)
{
- case SlimeTypeBlue: new SlimeEntity(x, y, SlimeTypeBlue, true); break;
- case SlimeTypeRed: new SlimeEntity(x, y, SlimeTypeRed, true); break;
- case SlimeTypeViolet: new SlimeEntity(x, y, SlimeTypeViolet, true); break;
- default: new SlimeEntity(x, y, SlimeTypeStandard, true); break;
+ case SlimeTypeBlue: slime = new SlimeEntity(x, y, SlimeTypeBlue, true); break;
+ case SlimeTypeRed: slime = new SlimeEntity(x, y, SlimeTypeRed, true); break;
+ case SlimeTypeViolet: slime = new SlimeEntity(x, y, SlimeTypeViolet, true); break;
+ default: slime = new SlimeEntity(x, y, SlimeTypeStandard, true); break;
}
+ slime->disableCollidingTemporary();
}
}
game().makeShake(1.0f);
SoundManager::getInstance().playSound(SOUND_SLIME_SMASH);
ItemEntity* newItem = new ItemEntity(ItemSilverCoin, x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_GIANT_SLIME), x, y, 128, 128, 8);
star->setFrame(4);
star->setFading(true);
star->setZ(y+ 100);
star->setAge(-0.4f);
star->setLifetime(0.3f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
void LargeSlimeEntity::render(sf::RenderTarget* app)
{
if (!isDying)
{
// shadow
sprite.setPosition(x, y);
if (isMirroring)
sprite.setTextureRect(sf::IntRect(shadowFrame * width + width, 0, -width, height));
else
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame % 5) * width + width, (frame / 5) * height, -width, height));
else
sprite.setTextureRect(sf::IntRect((frame % 5) * width, (frame / 5) * height, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void LargeSlimeEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
- inflictsRecoilTo(entity);
+ inflictsRepulsionTo(entity);
}
}
-void LargeSlimeEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void LargeSlimeEntity::inflictsRepulsionTo(BaseCreatureEntity* targetEntity)
{
if (state == 7)
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), KING_RAT_RUNNING_RECOIL );
- targetEntity->giveRecoil(true, recoilVector, 1.0f);
+ targetEntity->giveRepulsion(true, recoilVector, 1.0f);
}
}
bool LargeSlimeEntity::canCollide()
{
return h <= 70.0f;
}
BaseCreatureEntity::enumMovingStyle LargeSlimeEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
void LargeSlimeEntity::fire()
{
SoundManager::getInstance().playSound(fireSound);
EnemyBoltEntity* bolt = new EnemyBoltEntity(x, y + 10, fireType, 0, enemyType);
bolt->setDamages(fireDamage);
bolt->setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(),GIANT_SLIME_FIRE_VELOCITY ));
}
diff --git a/src/LargeSlimeEntity.h b/src/LargeSlimeEntity.h
index fe96875..5cbb930 100644
--- a/src/LargeSlimeEntity.h
+++ b/src/LargeSlimeEntity.h
@@ -1,47 +1,47 @@
#ifndef LARGESLIMESPRITE_H
#define LARGESLIMESPRITE_H
#include "EnemyEntity.h"
#include "PlayerEntity.h"
#include "SlimeEntity.h"
class LargeSlimeEntity : public EnemyEntity
{
public:
LargeSlimeEntity(float x, float y, slimeTypeEnum slimeType);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void inflictsRepulsionTo(BaseCreatureEntity* targetEntity) override;
virtual bool canCollide();
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
void afterWallCollide();
void changeToState(int n);
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void dying();
virtual enumMovingStyle getMovingStyle();
private:
float timer;
int state, counter;
bool isFirstJumping;
bool isFalling;
float missileDelay;
slimeTypeEnum slimeType;
int fireSound;
int fireDamage;
enumShotType fireType;
int baseFrame;
void fire();
};
#endif // GIANTSLIMESPRITE_H
diff --git a/src/LittleSpiderEntity.cpp b/src/LittleSpiderEntity.cpp
index 53781ca..83e5fbb 100644
--- a/src/LittleSpiderEntity.cpp
+++ b/src/LittleSpiderEntity.cpp
@@ -1,142 +1,142 @@
#include "LittleSpiderEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "SpiderWebEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
LittleSpiderEntity::LittleSpiderEntity(float x, float y, EnumSpiderType spideType, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_LITTLE_SPIDER), x, y)
{
imagesProLine = 8;
creatureSpeed = 175.0f;
velocity = Vector2D(creatureSpeed * 0.25f);
meleeDamages = 5;
this->spideType = spideType;
if (spideType == SpiderTypeTarantula)
{
enemyType = invocated ? EnemyTypeSpiderTarantula_invocated : EnemyTypeSpiderTarantula;
frame = 8;
deathFrame = FRAME_CORPSE_LITTLE_SPIDER_TARANTULA;
dyingFrame = 11;
hp = 32;
webTimer = 3 + 0.1f * (float)(rand() % 50);
meleeType = ShotTypePoison;
meleeLevel = 1;
}
else
{
enemyType = invocated ? EnemyTypeSpiderLittle_invocated : EnemyTypeSpiderLittle;
frame = 0;
deathFrame = FRAME_CORPSE_LITTLE_SPIDER;
dyingFrame = 3;
hp = 16;
}
type = invocated ? ENTITY_ENEMY_INVOCATED : ENTITY_ENEMY;
bloodColor = BloodGreen;
shadowFrame = 4;
agonizingSound = SOUND_SPIDER_LITTLE_DIE;
timer = -1.0f; //(rand() % 50) / 10.0f;
if (invocated) age = 0.0f;
sprite.setOrigin(32.0f, 40.0f);
resistance[ResistancePoison] = ResistanceImmune;
roaming = true;
}
void LittleSpiderEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
timer = timer - delay;
if (timer <= 0.0f)
{
timer = (rand() % 50) / 10.0f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
roaming = false;
}
frame = ((int)(age * (roaming ? 1.5f : 5.0f))) % 3;
if (spideType == SpiderTypeTarantula)
frame += 8;
if (spideType == SpiderTypeTarantula)
{
webTimer = webTimer - delay;
if (webTimer <= 0.0f)
{
webTimer = 3 + 0.1f * (float)(rand() % 50);
new SpiderWebEntity(x, y, false);
}
}
}
EnemyEntity::animate(delay);
z = y + 21;
}
void LittleSpiderEntity::calculateBB()
{
boundingBox.left = (int)x - 18;
boundingBox.width = 36;
boundingBox.top = (int)y - 10;
boundingBox.height = 32;
}
void LittleSpiderEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
computeFacingDirection();
}
void LittleSpiderEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
computeFacingDirection();
}
void LittleSpiderEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
computeFacingDirection();
}
void LittleSpiderEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
computeFacingDirection();
}
void LittleSpiderEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking )
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 100.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
computeFacingDirection();
}
}
void LittleSpiderEntity::drop()
{
if (type == ENTITY_ENEMY)
EnemyEntity::drop();
}
diff --git a/src/MessageGenerator.h b/src/MessageGenerator.h
index 7a883e9..63b4c93 100644
--- a/src/MessageGenerator.h
+++ b/src/MessageGenerator.h
@@ -1,118 +1,121 @@
/** This file is part of Witch Blast.
*
* Witch Blast is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Witch Blast is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MESSAGEGENERATOR_H_INCLUDED
#define MESSAGEGENERATOR_H_INCLUDED
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <sstream>
#include <map>
struct messageBuilderStruct
{
EnumMessagesType type;
int icon;
std::string key;
int nbLines;
};
const float MESSAGE_DURATION = 8.0f;
const float MESSAGE_DURATION_SHORT = 4.0f;
std::map<EnumMessages, messageBuilderStruct> msgMap =
{
{ MsgInfoLevel1, { MessageTypeInfo, 1, "msg_level1", 3} },
{ MsgInfoLevel2, { MessageTypeInfo, 1, "msg_level2", 3} },
{ MsgInfoLevel3, { MessageTypeInfo, 1, "msg_level3", 3} },
{ MsgInfoLevel4, { MessageTypeInfo, 1, "msg_level4", 3} },
{ MsgInfoLevel5, { MessageTypeInfo, 1, "msg_level5", 3} },
{ MsgInfoLevel6, { MessageTypeInfo, 1, "msg_level6", 3} },
{ MsgInfoLevel7, { MessageTypeInfo, 1, "msg_level7", 3} },
{ MsgInfoRatsBats, { MessageTypeInfo, 1, "msg_rats_bats", 3} },
{ MsgInfoSnakes, { MessageTypeInfo, 1, "msg_snakes", 2} },
{ MsgInfoWitches, { MessageTypeInfo, 1, "msg_witches", 2} },
{ MsgInfoGold, { MessageTypeInfo, 1, "msg_gold", 3} },
{ MsgInfoFamiliar, { MessageTypeInfo, 1, "msg_familiar", 3} },
{ MsgInfoButcher, { MessageTypeInfo, 1, "msg_butcher", 3} },
{ MsgInfoGiantSlime, { MessageTypeInfo, 1, "msg_giant_slime", 3} },
{ MsgInfoCyclops, { MessageTypeInfo, 1, "msg_cyclops", 3} },
{ MsgInfoWererat, { MessageTypeInfo, 1, "msg_wererat", 2} },
{ MsgInfoGiantSpiderBefore, { MessageTypeInfo, 1, "msg_giant_spider_before", 2} },
{ MsgInfoGiantSpiderAfter, { MessageTypeInfo, 1, "msg_giant_spider_after", 2} },
{ MsgInfoFranky, { MessageTypeInfo, 1, "msg_francky", 2} },
{ MsgInfoVampire, { MessageTypeInfo, 1, "msg_vampire", 2} },
{ MsgInfoDivHealer, { MessageTypeInfo, 0, "divinity_healer", 3} },
{ MsgInfoDivFighter, { MessageTypeInfo, 0, "divinity_fighter", 3} },
{ MsgInfoDivIce, { MessageTypeInfo, 0, "divinity_ice", 3} },
{ MsgInfoDivStone, { MessageTypeInfo, 0, "divinity_stone", 3} },
{ MsgInfoDivAir, { MessageTypeInfo, 0, "divinity_air", 3} },
+ { MsgInfoDivIllusion, { MessageTypeInfo, 0, "divinity_illusion", 3} },
+ { MsgInfoDivFire, { MessageTypeInfo, 0, "divinity_fire", 3} },
+ { MsgInfoDivPoison, { MessageTypeInfo, 0, "divinity_poison", 3} },
{ MsgInfoDivIntervention, { MessageTypeInfo, 0, "divinity_intervention", 2} },
{ MsgInfoDivGift, { MessageTypeInfo, 0, "divinity_gift", 2} },
{ MsgInfoDivLevel, { MessageTypeInfo, 0, "divinity_leveling", 2} },
{ MsgInfoPotionId, { MessageTypeInfo, 0, "msg_potion_id", 2} },
{ MsgTutoBasics, { MessageTypeTutorial, 0, "msg_tuto_intro", 3} },
{ MsgTutoTips, { MessageTypeTutorial, 0, "msg_tuto_tips", 3} },
{ MsgTutoItems, { MessageTypeTutorial, 0, "msg_tuto_items", 3} },
{ MsgTutoShops, { MessageTypeTutorial, 0, "msg_tuto_shops", 3} },
{ MsgTutoBossDoor, { MessageTypeTutorial, 0, "msg_tuto_boss_door", 3} },
{ MsgTutoChallengeDoor, { MessageTypeTutorial, 0, "msg_tuto_challenge_door", 3} },
{ MsgTutoTemple, { MessageTypeTutorial, 0, "msg_tuto_temple", 3} },
{ MsgTutoHeal, { MessageTypeTutorial, 0, "msg_tuto_heal", 3} },
{ MsgTutoShots, { MessageTypeTutorial, 0, "msg_tuto_shots", 3} },
{ MsgTutoSpell, { MessageTypeTutorial, 0, "msg_tuto_spell", 3} },
{ MsgTutoAchievements, { MessageTypeTutorial, 0, "msg_tuto_achiev", 3} },
{ MsgTutoConsumables, { MessageTypeTutorial, 0, "msg_tuto_consumables", 3} },
{ MsgTutoPotions, { MessageTypeTutorial, 0, "msg_tuto_potions", 3} },
};
static void loadMessageData(std::string msg_array[NB_MSG_LINES], std::string key, int nbLines)
{
for (int i = 0; i < nbLines; i++)
{
std::stringstream ss;
ss << key << "_" << i;
msg_array[i] = tools::getLabel(ss.str());
}
}
static messageStruct getMessage(EnumMessages type)
{
messageStruct msg;
messageBuilderStruct msgBuilder = msgMap[type];
// init
msg.messageType = msgBuilder.type; //MessageTypeInfo;
msg.type = type;
msg.timer = msgBuilder.nbLines == 3 ? MESSAGE_DURATION : MESSAGE_DURATION_SHORT;
msg.timerMax = msg.timer;
msg.message[0] = "";
msg.message[1] = "";
msg.message[2] = "";
loadMessageData(msg.message, msgBuilder.key, msgBuilder.nbLines);
return msg;
}
#endif // MESSAGEGENERATOR_H_INCLUDED
diff --git a/src/ObstacleEntity.cpp b/src/ObstacleEntity.cpp
index 96cfc9a..6cc4941 100644
--- a/src/ObstacleEntity.cpp
+++ b/src/ObstacleEntity.cpp
@@ -1,204 +1,205 @@
#include "BoltEntity.h"
#include "EnemyBoltEntity.h"
#include "ExplosionEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "ObstacleEntity.h"
ObstacleEntity::ObstacleEntity(float x, float y, int objectFrame)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_DESTROYABLE), x, y)
{
imagesProLine = 3;
type = ENTITY_ENEMY_NC;
enemyType = EnemyTypeDestroyable;
movingStyle = movWalking;
explosive = false;
dyingSound = SOUND_BARREL_SMASH;
hurtingSound = SOUND_BARREL_HIT;
bloodColor = BloodNone;
obstacleBloodType = BloodBarrel;
deathFrame = FRAME_CORPSE_SLIME_VIOLET;
age = 0.0f;
frame = 0;
objectIndex = objectFrame;
xGrid = x / TILE_WIDTH;
yGrid = y / TILE_HEIGHT;
game().getCurrentMap()->setObjectTile(xGrid, yGrid, objectFrame);
game().getCurrentMap()->setLogicalTile(xGrid, yGrid, LogicalDestroyable);
// hp
if (objectIndex == MAPOBJ_BARREL || objectIndex == MAPOBJ_BARREL_NO_DROP
|| objectIndex == MAPOBJ_BARREL_EXPL || objectIndex == MAPOBJ_SKULL)
{
hp = 18;
hpMax = 18;
}
else if (objectIndex == MAPOBJ_BARREL + 1 || objectIndex == MAPOBJ_BARREL_NO_DROP + 1
|| objectIndex == MAPOBJ_BARREL_EXPL + 1 || objectIndex == MAPOBJ_SKULL + 1)
{
hp = 12;
hpMax = 12;
}
else if (objectIndex == MAPOBJ_BARREL + 2 || objectIndex == MAPOBJ_BARREL_NO_DROP + 2
|| objectIndex == MAPOBJ_BARREL_EXPL + 2 || objectIndex == MAPOBJ_SKULL + 2)
{
hp = 6;
hpMax = 6;
}
if (objectIndex >= MAPOBJ_BARREL_EXPL && objectIndex < MAPOBJ_BARREL_EXPL + 3)
{
obstacleBloodType = BloodBarrelPowder;
explosive = true;
initialFrame = 3;
initialObjectIndex = MAPOBJ_BARREL_EXPL;
frame = 3 + objectIndex - MAPOBJ_BARREL_EXPL;
}
else if (objectIndex >= MAPOBJ_BARREL && objectIndex < MAPOBJ_BARREL + 3)
{
initialFrame = 0;
initialObjectIndex = MAPOBJ_BARREL;
frame = objectIndex - MAPOBJ_BARREL;
}
else if (objectIndex >= MAPOBJ_BARREL_NO_DROP && objectIndex < MAPOBJ_BARREL_NO_DROP + 3)
{
initialFrame = 0;
initialObjectIndex = MAPOBJ_BARREL_NO_DROP;
frame = objectIndex - MAPOBJ_BARREL_NO_DROP;
}
else if (objectIndex >= MAPOBJ_SKULL && objectIndex < MAPOBJ_SKULL + 3)
{
obstacleBloodType = BloodSkull;
initialFrame = 6;
initialObjectIndex = MAPOBJ_SKULL;
frame = 6 + objectIndex - MAPOBJ_SKULL;
}
resistance[ResistanceFrozen] = ResistanceImmune;
- resistance[ResistanceRecoil] = ResistanceImmune;
+ resistance[ResistanceRepulsion] = ResistanceImmune;
resistance[ResistanceFire] = ResistanceVeryLow;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
}
int ObstacleEntity::getObjectIndex()
{
return objectIndex;
}
void ObstacleEntity::animate(float delay)
{
+ age += delay;
testSpriteCollisions();
}
void ObstacleEntity::render(sf::RenderTarget* app)
{
EnemyEntity::render(app);
}
void ObstacleEntity::dying()
{
EnemyEntity::dying();
game().getCurrentMap()->setObjectTile(xGrid, yGrid, 0);
game().getCurrentMap()->setLogicalTile(xGrid, yGrid, LogicalFloor);
for (int i = 0; i < 10; i++)
game().generateBlood(x, y, obstacleBloodType);
- if (explosive)
+ if (explosive && age > 2.0f)
{
new ExplosionEntity(x, y, ExplosionTypeStandard, 16, EnemyTypeNone, true);
SoundManager::getInstance().playSound(SOUND_BOOM_00);
game().addCorpse(x, y, deathFrame);
}
}
void ObstacleEntity::calculateBB()
{
boundingBox.left = (int)x - 30;
boundingBox.width = 60;
boundingBox.top = (int)y - 30;
boundingBox.height = 60;
}
void ObstacleEntity::drop()
{
if (initialObjectIndex == MAPOBJ_BARREL || initialObjectIndex == MAPOBJ_SKULL)
EnemyEntity::drop();
}
void ObstacleEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (entity == this) return;
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_BOLT )
{
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (!boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
EnemyEntity::collideWithBolt(boltEntity);
correctFrame();
}
}
else if (entity->getType() == ENTITY_ENEMY_BOLT )
{
EnemyBoltEntity* boltEntity = dynamic_cast<EnemyBoltEntity*>(entity);
if (!boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
EnemyEntity::collideWithBolt(boltEntity);
correctFrame();
}
}
else if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX)
{
EnemyEntity* enemyEntity = dynamic_cast<EnemyEntity*>(entity);
if (!enemyEntity->getDying() && enemyEntity->canCollide()&& enemyEntity->getMovingStyle() != movFlying)
{
hurt(getHurtParams(hp, ShotTypeStandard, 0, false, SourceTypeMelee, enemyEntity->getEnemyType(), false));
}
}
}
}
void ObstacleEntity::correctFrame()
{
if (hp > 0)
{
if ( (hp - 1) / 6 == 1)
{
frame = initialFrame + 1;
objectIndex = initialObjectIndex + 1;
}
else if ( (hp - 1) / 6 == 0)
{
frame = initialFrame + 2;
objectIndex = initialObjectIndex + 2;
}
game().getCurrentMap()->setObjectTile(xGrid, yGrid, objectIndex);
}
}
int ObstacleEntity::hurt(StructHurt hurtParam)
{
int oldHp = hp;
int result = EnemyEntity::hurt(hurtParam);
int diff = oldHp - hp;
for (int i = 0; i < diff; i++)
game().generateBlood(x, y, obstacleBloodType);
return result;
}
diff --git a/src/PlayerEntity.cpp b/src/PlayerEntity.cpp
index 2b72e73..cc227c0 100644
--- a/src/PlayerEntity.cpp
+++ b/src/PlayerEntity.cpp
@@ -1,3766 +1,3989 @@
#include "PlayerEntity.h"
#include "SlimeEntity.h"
#include "SlimePetEntity.h"
#include "FallingRockEntity.h"
#include "BoltEntity.h"
#include "SpiderWebEntity.h"
#include "EvilFlowerEntity.h"
#include "EnemyBoltEntity.h"
#include "ItemEntity.h"
#include "FairyEntity.h"
+#include "ExplosionEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextEntity.h"
#include "TextMapper.h"
#include <iostream>
#include <sstream>
#define SPIRAL_STAIRCASE
const int xHalo[9][3] =
{
{ 13, 13, 18},
{ 47, 45, 37},
{ 48, 47, 44},
{ 15, 15, 16},
{ 45, 45, 45},
{ 46, 44, 42},
{ 13, 13, 13},
{ 13, 24, 36},
{ 13, 17, 13}
};
const int yHalo[9][3] =
{
{ 26, 25, 25},
{ 23, 23, 23},
{ 23, 24, 25},
{ 31, 32, 34},
{ 23, 23, 23},
{ 23, 22, 21},
{ 26, 26, 26},
{ 26, 15, 6},
{ 25, 27, 25}
};
const int KEYS_MOVE_TOLERANCE = 36;
PlayerEntity::PlayerEntity(float x, float y)
: BaseCreatureEntity (ImageManager::getInstance().getImage(IMAGE_PLAYER_0), x, y, 64, 96)
{
currentFireDelay = -1.0f;
randomFireDelay = -1.0f;
rageFireDelay = 1.0f;
invincibleDelay = -1.0f;
divineInterventionDelay = -1.0f;
showCone = false;
fireAnimationDelay = -1.0f;
fireAnimationDelayMax = 0.4f;
spellAnimationDelay = -1.0f;
spellAnimationDelayMax = 0.7f;
canFirePlayer = true;
type = ENTITY_PLAYER;
imagesProLine = 8;
playerStatus = playerStatusPlaying;
hp = INITIAL_PLAYER_HP;
#ifdef TEST_MODE
hp = INITIAL_PLAYER_HP * 100;
#endif // TEST_MODE
hpDisplay = hp;
hpMax = hp;
gold = 0;
donation = 0;
endAge = 0.0f;
hiccupDelay = HICCUP_DELAY;
idleAge = 0.0f;
boltLifeTime = INITIAL_BOLT_LIFE;
specialBoltTimer = -1.0f;
bloodColor = BloodRed;
canExplode = false;
// init the equipment (to empty)
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++) equip[i] = false;
collidingDirection = 5;
// init the shots (to none)
for (int i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
specialShots[i] = ShotTypeStandard;
specialShotLevel[i] = 0;
}
// init the consumibles
for (int i = 0; i < MAX_SLOT_CONSUMABLES; i++)
consumable[i] = -1;
// init the lost HP
for (int i = 0; i < LAST_LEVEL; i++)
lostHp[i] = 0;
specialShotIndex = 0;
needInitShotType = false;
computePlayer();
firingDirection = 5;
facingDirection = 2;
keyDirection = 5;
canAnimateFire = true;
sprite.setOrigin(32, 80);
protection.active = false;
armor = 0.0f;
activeSpell.delay = -1.0f;
activeSpell.spell = SpellNone;
divinity.divinity = -1;
divinity.piety = 0;
divinity.level = 0;
divinity.interventions = 0;
divinity.percentsToNextLevels = 0.0f;
shouldBeSavedFromDivinity = false;
isRegeneration = false;
isFairyTransmuted = false;
itemToBuy = NULL;
+ lastTeleportSave = -15.0f;
}
void PlayerEntity::moveTo(float newX, float newY)
{
float dx = newX - x;
float dy = newY - y;
x = newX;
y = newY;
for(int unsigned i = 0; i < fairies.size(); i++)
{
fairies[i]->setX(fairies[i]->getX() + dx);
fairies[i]->setY(fairies[i]->getY() + dy);
// Keep fairies on screen
if (fairies[i]->getX() < 0)
fairies[i]->setX(0);
else if (fairies[i]->getX() > MAP_WIDTH * TILE_WIDTH)
fairies[i]->setX(MAP_WIDTH * TILE_WIDTH);
if (fairies[i]->getY() < 0)
fairies[i]->setY(0);
else if (fairies[i]->getY() > MAP_HEIGHT * TILE_HEIGHT)
fairies[i]->setY(MAP_HEIGHT * TILE_HEIGHT);
}
}
int PlayerEntity::getFacingDirection()
{
return facingDirection;
}
void PlayerEntity::setFacingDirection(int facingDirection)
{
if (facingDirection == 4 || facingDirection == 6 || facingDirection == 2 || facingDirection == 8)
this->facingDirection = facingDirection;
}
float PlayerEntity::getPercentFireDelay()
{
if (canFirePlayer) return 1.0f;
else return (1.0f - currentFireDelay / fireDelay);
}
float PlayerEntity::getFadingDivinity(bool showCone)
{
if (playerStatus == playerStatusPraying)
{
float result = 1.0f;
if (statusTimer < 0.25f)
result = 4 * statusTimer;
else if (statusTimer > WORSHIP_DELAY - 0.25f)
result = (WORSHIP_DELAY - statusTimer) * 4;
return result;
}
if (!this->showCone && showCone) return -1.0f;
if (divineInterventionDelay <= 0.0f) return -1.0f;
if (!this->showCone)
{
if (int(age * 12) % 2 == 0) return 1.0f;
else return 0.3f;
}
else if (playerStatus == playerStatusPraying)
{
float result = 1.0f;
if (statusTimer < 0.25f)
result = 4 * statusTimer;
else if (statusTimer > WORSHIP_DELAY - 0.25f)
result = (WORSHIP_DELAY - statusTimer) * 4;
return result;
}
else
{
float result = 1.0f;
if (divineInterventionDelay < 0.25f)
result = 4 * divineInterventionDelay;
else if (divineInterventionDelay > WORSHIP_DELAY - 0.25f)
result = (WORSHIP_DELAY - divineInterventionDelay) * 4;
if (isRegeneration) result *= 0.4f;
return result;
}
}
float PlayerEntity::getPercentSpellDelay()
{
if (activeSpell.spell == SpellNone)
return getPercentFireDelay();
else
{
if (activeSpell.delay <= 0.0f) return 1.0f;
else return (1.0f - activeSpell.delay / activeSpell.delayMax);
}
}
int PlayerEntity::getLostHp(int level)
{
if (level >= 1 && level <= LAST_LEVEL)
return (lostHp[level - 1]);
else
return 0;
}
void PlayerEntity::setLostHp(int level, int n)
{
if (level >= 1 && level <= LAST_LEVEL)
lostHp[level - 1] = n;
}
int PlayerEntity::getDamage()
{
- return fireDamages;
+ return fireDamage;
+}
+
+int PlayerEntity::getDps()
+{
+ return dps;
}
bool PlayerEntity::isPoisoned()
{
return (specialState[SpecialStatePoison].active);
}
int PlayerEntity::getCollidingDirection()
{
return collidingDirection;
}
PlayerEntity::playerStatusEnum PlayerEntity::getPlayerStatus()
{
return playerStatus;
}
void PlayerEntity::setPlayerStatus(PlayerEntity::playerStatusEnum playerStatus)
{
this->playerStatus = playerStatus;
}
bool PlayerEntity::isDead()
{
return playerStatus == playerStatusDead;
}
enemyTypeEnum PlayerEntity::getLastHurtingEnemy()
{
return lastHurtingEnemy;
}
sourceTypeEnum PlayerEntity::getLastHurtingSource()
{
return lastHurtingSource;
}
float PlayerEntity::getEndAge()
{
return endAge;
}
void PlayerEntity::setEndAge(float endAge)
{
this->endAge = endAge;
}
bool PlayerEntity::getFairyTransmuted()
{
return isFairyTransmuted;
}
divinityStruct PlayerEntity::getDivinity()
{
return divinity;
}
int PlayerEntity::getPiety()
{
return divinity.piety;
}
void PlayerEntity::stuck()
{
if (playerStatus != playerStatusEntering)
castTeleport();
}
void PlayerEntity::setEntering()
{
playerStatus = playerStatusEntering;
}
void PlayerEntity::setLeavingLevel()
{
playerStatus = playerStatusGoingNext;
if (game().getLevel() <= LAST_LEVEL)
{
if (getLostHp(game().getLevel()) == 0)
{
game().registerAchievement(AchievementNoDamage);
int counter = 0;
for (int i = 1; i <= game().getLevel(); i++) if (getLostHp(i) == 0) counter++;
if (counter >= 2) game().registerAchievement(AchievementNoDamage2);
if (counter >= 3) game().registerAchievement(AchievementNoDamage3);
}
}
}
float PlayerEntity::getFireRate()
{
return 1.0f / fireDelay;
}
void PlayerEntity::pay(int price)
{
gold -= price;
displayAcquiredGold(-price);
if (gold < 0) gold = 0;
SoundManager::getInstance().playSound(SOUND_PAY);
}
void PlayerEntity::acquireItemAfterStance()
{
if (acquiredItem >= FirstEquipItem)
{
equip[acquiredItem - FirstEquipItem] = true;
if (acquiredItem != ItemBossKey) game().proceedEvent(EventGetItem);
// familiar
if (items[acquiredItem].familiar != FamiliarNone)
{
setEquipped(acquiredItem - FirstEquipItem, true);
game().proceedEvent(EventGetFamiliar);
}
// shot types
else if (items[acquiredItem].specialShot != (ShotTypeStandard))
{
registerSpecialShot(acquiredItem);
game().proceedEvent(EventGetSpecialShot);
}
// spells
else if (items[acquiredItem].spell != SpellNone)
{
setActiveSpell(items[acquiredItem].spell, false);
game().proceedEvent(EventGetSpell);
}
// pet slime
else if (acquiredItem == ItemPetSlime)
{
new SlimePetEntity();
}
// alchemy book
else if (acquiredItem == ItemBookAlchemy)
{
game().acquireAlchemyBook();
}
// floor item
else if (acquiredItem == ItemFloorMap)
game().revealFloor();
else if (acquiredItem == ItemAlcohol)
hiccupDelay = HICCUP_DELAY;
// acquirement
if (equip[EQUIP_DISPLACEMENT_GLOVES] && equip[EQUIP_LEATHER_BOOTS] && equip[EQUIP_MAGICIAN_HAT] && equip[EQUIP_MAGICIAN_ROBE])
game().registerAchievement(AchievementCompleteSet);
computePlayer();
}
else
{
if (acquiredItem == ItemBossHeart)
{
int hpBonus = 2 + rand() % 4;
hpMax += hpBonus;
hp += hpBonus;
hpDisplay += hpBonus;
SoundManager::getInstance().playSound(SOUND_EAT);
std::ostringstream oss;
oss << "HP Max +" << hpBonus;
TextEntity* text = new TextEntity(oss.str(), 15, x, y - 50.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAlignment(ALIGN_CENTER);
text->setAge(-1.0f);
text->setLifetime(1.2f);
text->setWeight(-60.0f);
text->setType(ENTITY_FLYING_TEXT);
text->setZ(2000);
}
else if (acquiredItem == ItemBonusHealth)
{
hpMax += 1;
hp = hpMax;
SoundManager::getInstance().playSound(SOUND_EAT);
TextEntity* text = new TextEntity("HP Max + 1", 15, x, y - 50.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAlignment(ALIGN_CENTER);
text->setAge(-1.0f);
text->setLifetime(1.2f);
text->setWeight(-60.0f);
text->setType(ENTITY_FLYING_TEXT);
text->setZ(2000);
}
}
spriteItem->setDying(true);
spriteItemStar->setDying(true);
playerStatus = playerStatusPlaying;
}
void PlayerEntity::resetFloorItem()
{
equip[EQUIP_BOSS_KEY] = false;
equip[EQUIP_FLOOR_MAP] = false;
equip[EQUIP_ALCOHOL] = false;
equip[EQUIP_FAIRY_POWDER] = false;
equip[EQUIP_LUCK] = false;
computePlayer();
}
void PlayerEntity::setItemToBuy(ItemEntity* item)
{
itemToBuy = item;
}
ItemEntity* PlayerEntity::getItemToBuy()
{
return itemToBuy;
}
void PlayerEntity::animate(float delay)
{
// shot timer
if (specialBoltTimer >= 0.0f)
{
specialBoltTimer -= delay;
if (specialBoltTimer <= 0.0f)
{
if (getShotType() == ShotTypeIce) SoundManager::getInstance().playSound(SOUND_ICE_CHARGE);
}
}
if (playerStatus == playerStatusGoingNext)
{
return;
}
if (playerStatus == playerStatusStairs)
{
x += velocity.x * delay;
y += velocity.y * delay;
age += delay;
frame = ((int)(age * 7.0f)) % 4;
if (frame == 3) frame = 1;
if (x < (MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2)
{
facingDirection = 8;
game().moveToOtherMap(8);
}
return;
}
// rate of fire
if (!canFirePlayer)
{
currentFireDelay -= delay;
canFirePlayer = (currentFireDelay <= 0.0f);
}
if (randomFireDelay >= 0.0f) randomFireDelay -= delay;
// spells
if (activeSpell.spell != SpellNone && activeSpell.delay > 0.0f)
{
if (game().getCurrentMap()->isCleared())
activeSpell.delay -= 40 * delay;
else
activeSpell.delay -= delay;
if (activeSpell.spell == SpellProtection && protection.active)
activeSpell.delay = activeSpell.delayMax;
if (activeSpell.delay <= 0.0f) SoundManager::getInstance().playSound(SOUND_SPELL_CHARGE);
}
// protection
if (protection.active)
{
protection.timer -= delay;
if (protection.timer <= 0.0f)
{
protection.active = false;
computePlayer();
}
}
// acquisition animation
if (playerStatus == playerStatusAcquire)
{
statusTimer -= delay;
if (statusTimer <= 0.0f)
{
acquireItemAfterStance();
}
}
else if (equip[EQUIP_ALCOHOL] && playerStatus == playerStatusPlaying)
{
hiccupDelay -= delay;
if (hiccupDelay <= 0.0f)
{
hiccupDelay = 4.0f;
// hiccup
- recoil.active = true;
- recoil.stun = true;
- recoil.velocity = Vector2D(350.0f);
- recoil.timer = 0.4f;
+ repulsion.active = true;
+ repulsion.stun = true;
+ repulsion.velocity = Vector2D(350.0f);
+ repulsion.timer = 0.4f;
for (int i = 0; i < 4; i++)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypePoison, 0);
bolt->setDamages(4);
bolt->setFlying(isFairyTransmuted);
- //bolt->setVelocity(recoil.velocity.vectorTo(Vector2D(0, 0), fireVelocity));
bolt->setVelocity(Vector2D(fireVelocity));
}
TextEntity* text = new TextEntity("*hic*", 16, x, y - 30.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
SoundManager::getInstance().playSound(SOUND_HICCUP);
SoundManager::getInstance().playSound(SOUND_BLAST_STANDARD);
}
}
if (divineInterventionDelay > 0.0f) divineInterventionDelay -= delay;
if (fireAnimationDelay > 0.0f)
{
fireAnimationDelay -= delay;
if (fireAnimationDelay <= 0.0f) fireAnimationDelay = -1.0f;
}
if (spellAnimationDelay > 0.0f)
{
spellAnimationDelay -= delay;
if (spellAnimationDelay <= 0.0f) spellAnimationDelay = -1.0f;
}
// unlocking animation
else if (playerStatus == playerStatusUnlocking || playerStatus == playerStatusPraying)
{
statusTimer -= delay;
if (statusTimer <= 0.0f)
{
playerStatus = playerStatusPlaying;
}
}
if (playerStatus == playerStatusDead || playerStatus == playerStatusVictorious)
{
endAge += delay;
velocity = Vector2D(0.0f, 0.0f);
}
else
testSpriteCollisions();
// key room collision
if (game().getCurrentMap()->getRoomType() == roomTypeKey && !game().getCurrentMap()->isCleared())
{
sf::IntRect col1;
col1.width = 198;
col1.height = 68;
col1.top = 254;
col1.left = 380;
sf::IntRect col2;
col2.width = 68;
col2.height = 198;
col2.top = 189;
col2.left = 445;
if (boundingBox.intersects(col1) || boundingBox.intersects(col2))
{
- recoil.active = true;
- recoil.stun = true;
- recoil.velocity = Vector2D(GAME_WIDTH / 2, GAME_HEIGHT /2).vectorTo(Vector2D(x, y), 650.0f);
- recoil.timer = 0.4f;
+ repulsion.active = true;
+ repulsion.stun = true;
+ repulsion.velocity = Vector2D(GAME_WIDTH / 2, GAME_HEIGHT /2).vectorTo(Vector2D(x, y), 650.0f);
+ repulsion.timer = 0.4f;
game().activateKeyRoomEffect(true);
}
}
//collidingDirection = 0;
BaseCreatureEntity::animate(delay);
if (firingDirection != 5)
facingDirection = firingDirection;
// find the frame
if (firingDirection != 5 && canAnimateFire)
{
if (fireAnimationDelay < 0.0f)
fireAnimationDelay = fireAnimationDelayMax;
fireAnimationDirection = firingDirection;
}
else if (isMoving())
{
frame = ((int)(age * 7.0f)) % 4;
if (frame == 3) frame = 1;
}
else if (playerStatus == playerStatusAcquire || playerStatus == playerStatusUnlocking || playerStatus == playerStatusPraying)
frame = 3;
else if (playerStatus == playerStatusDead)
frame = 0;
else // standing
{
frame = 1;
}
if (playerStatus != playerStatusPlaying || isMoving() || firingDirection != 5)
idleAge = 0.0f;
else
idleAge += delay;
if (x < 0)
game().moveToOtherMap(4);
else if (x > MAP_WIDTH * TILE_WIDTH)
game().moveToOtherMap(6);
else if (y < 0)
game().moveToOtherMap(8);
else if (y > MAP_HEIGHT * TILE_HEIGHT)
game().moveToOtherMap(2);
#ifdef SPIRAL_STAIRCASE
else if (playerStatus == playerStatusPlaying
&& game().getLevel() < LAST_LEVEL
&& game().getCurrentMap()->getRoomType() == roomTypeExit && y < TILE_HEIGHT * 0.6f)
{
playerStatus = playerStatusStairs;
velocity.y = creatureSpeed / 12;
velocity.x = -creatureSpeed / 3;
facingDirection = 4;
SpriteEntity* exitDoorEntity = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_TILES),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorEntity->setZ(TILE_HEIGHT);
exitDoorEntity->setImagesProLine(24);
exitDoorEntity->setFrame(MAP_WALL_BEGIN + 21 + 24 * game().getCurrentMap()->getWallType());
exitDoorEntity->setType(ENTITY_EFFECT);
SpriteEntity* exitDoorAroundEntity = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_DOORS),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorAroundEntity->setZ(TILE_HEIGHT + 1);
exitDoorAroundEntity->setImagesProLine(8);
exitDoorAroundEntity->setFrame(5 + 7 * 8);
exitDoorAroundEntity->setType(ENTITY_EFFECT);
SpriteEntity* exitDoorEntityShadow = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_TILES_SHADOW),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorEntityShadow->setZ(TILE_HEIGHT + 2);
exitDoorEntityShadow->setImagesProLine(10);
exitDoorEntityShadow->setFrame(4);
exitDoorEntityShadow->setType(ENTITY_EFFECT);
}
#endif
if (playerStatus == playerStatusEntering)
{
if (boundingBox.left > TILE_WIDTH
&& (boundingBox.left + boundingBox.width) < TILE_WIDTH * (MAP_WIDTH - 1)
&& boundingBox.top > TILE_HEIGHT
&& (boundingBox.top + boundingBox.height) < TILE_HEIGHT * (MAP_HEIGHT - 1))
{
playerStatus = playerStatusPlaying;
game().closeDoors();
}
else
{
if (x < 2 * TILE_WIDTH)
{
velocity.x = creatureSpeed;
velocity.y = 0.0f;
}
else if (x > (MAP_WIDTH - 3) * TILE_WIDTH)
{
velocity.x = -creatureSpeed;
velocity.y = 0.0f;
}
else if (y < 2 * TILE_HEIGHT)
{
velocity.y = creatureSpeed;
velocity.x = 0.0f;
}
else if (y > (MAP_HEIGHT - 3) * TILE_HEIGHT)
{
velocity.y = -creatureSpeed;
velocity.x = 0.0f;
}
}
}
if (playerStatus != playerStatusDead)
{
// effects
if (invincibleDelay >= 0.0f) invincibleDelay -= delay;
if (specialState[SpecialStateConfused].active)
SoundManager::getInstance().playSound(SOUND_VAMPIRE_HYPNOSIS, false);
// rage
if (specialState[SpecialStateRage].active)
{
specialState[SpecialStateRage].param3 -= delay;
if (specialState[SpecialStateRage].param3 <= 0.0f)
{
specialState[SpecialStateRage].param3 += specialState[SpecialStateRage].param2;
if (!equip[EQUIP_RAGE_AMULET]) rageFire(specialState[SpecialStateRage].param1, true, 1.5f);
}
}
if (equip[EQUIP_RAGE_AMULET])
{
rageFireDelay -= delay;
if (rageFireDelay <= 0.0f)
{
rageFireDelay += specialState[SpecialStateRage].active ? 1.5f : 8.0f;
rageFire(specialState[SpecialStateRage].param1, true, 1.5f);
}
}
}
z = y + 4;
}
bool PlayerEntity::canCollide()
{
return invincibleDelay <= 0.0f;
}
void PlayerEntity::setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2, bool waitUnclear)
{
BaseCreatureEntity::setSpecialState(state, active, timer, param1, param2, waitUnclear);
computePlayer();
}
void PlayerEntity::renderPlayer(sf::RenderTarget* app)
{
if (invincibleDelay > 0.0f)
{
if ((int)(age * 10.0f) % 2 == 0)return;
}
sf::Color savedColor = sprite.getColor();
if (isPoisoned()) sprite.setColor(sf::Color(180, 255, 180, 255));
// body
if (isMirroring)
sprite.setTextureRect(sf::IntRect( frame * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( frame * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
// boots
if (equip[EQUIP_BOOTS_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_LEATHER_BOOTS] && playerStatus != playerStatusDead)
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_ROBE_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (15 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (15 + frame) * width, spriteDy * height, width, height));
sprite.setColor(sf::Color(255, 255, 255, 100 + 100 * cosf(game().getAbsolutTime() * 3.5f)));
app->draw(sprite, sf::BlendAdd);
sprite.setColor(sf::Color(255, 255, 255, 255));
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_ROBE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_GLOVES_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_DISPLACEMENT_GLOVES])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_CRITICAL_ADVANCED])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
else if (equip[EQUIP_CRITICAL])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
if (equip[EQUIP_RAGE_AMULET])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
else if (equip[EQUIP_AMULET_RETALIATION])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_2));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (3 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (3 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
if (equip[EQUIP_BELT_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (27 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (27 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_LEATHER_BELT])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (15 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (15 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
// staff
int frameDx = equip[EQUIP_MAHOGANY_STAFF] ? 6 : 3;
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
if (equip[EQUIP_BLOOD_SNAKE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (27 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (27 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_REAR_SHOT_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_2));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_REAR_SHOT])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// shot type
if (getShotType() != ShotTypeStandard)
{
switch (getShotType())
{
case ShotTypeIce:
sprite.setColor(sf::Color(100, 220, 255, 255));
break;
case ShotTypeStone:
sprite.setColor(sf::Color(120, 120, 150, 255));
break;
case ShotTypeLightning:
sprite.setColor(sf::Color(255, 255, 0, 255));
break;
case ShotTypeIllusion:
sprite.setColor(sf::Color(240, 180, 250, 255));
break;
case ShotTypeStandard:
sprite.setColor(sf::Color(255, 255, 255, 0));
break;
case ShotTypeFire:
sprite.setColor(sf::Color(255, 180, 0, 255));
break;
case ShotTypePoison:
sprite.setColor(sf::Color(50, 255, 50, 255));
break;
default:
std::cout << "[WARNING] Can not render shot type: " << getShotType() << std::endl;
}
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (3 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (3 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// hat
if (equip[EQUIP_HAT_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_HAT] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (6 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (6 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
}
void PlayerEntity::renderHalo(sf::RenderTarget* app)
{
if (frame > 2 || spriteDy > 8) return;
// gems
if ((getShotType() == ShotTypeIce || getShotType() == ShotTypeLightning || getShotType() == ShotTypeFire || getShotType() == ShotTypePoison) && playerStatus != playerStatusDead)
{
int fade;
sf::Color savedColor = sprite.getColor();
if (getShotType() != ShotTypeIce || specialBoltTimer <= 0.0f) fade = 255;
else fade = ((STATUS_FROZEN_BOLT_DELAY[getShotLevel()] - specialBoltTimer) / STATUS_FROZEN_BOLT_DELAY[getShotLevel()]) * 128;
if (getShotType() == ShotTypeLightning)
fade = 150 + rand() % 105;
if (getShotType() == ShotTypeFire)
fade = 200 + rand() % 40;
if (getShotType() == ShotTypePoison)
fade = 150 + rand() % 40;
if (getShotType() == ShotTypeIce)
sprite.setTextureRect(sf::IntRect(448, 864, 20, 20));
else if (getShotType() == ShotTypeLightning)
sprite.setTextureRect(sf::IntRect(448 + 20, 864, 20, 20));
else if (getShotType() == ShotTypeFire)
sprite.setTextureRect(sf::IntRect(448 + 40, 864, 20, 20));
else if (getShotType() == ShotTypePoison)
sprite.setTextureRect(sf::IntRect(448, 864 + 20, 20, 20));
sprite.setColor(sf::Color(255, 255, 255, fade));
if (isMirroring)
sprite.setPosition(x - 10 + 64 - xHalo[spriteDy][frame], y - 10 + yHalo[spriteDy][frame]);
else
sprite.setPosition(x - 10 + xHalo[spriteDy][frame], y - 10 + yHalo[spriteDy][frame]);
sf::RenderStates r;
r.blendMode = sf::BlendAdd;
app->draw(sprite, r);
sprite.setPosition(x, y);
sprite.setColor(savedColor);
}
}
void PlayerEntity::render(sf::RenderTarget* app)
{
sprite.setPosition(x, y);
spriteDy = 0;
isMirroring = false;
if (idleAge > 8.5f)
{
idleAge -= 8.5f;
}
else if (idleAge >= 7.5)
{
spriteDy = 8;
frame = 2;
}
else if (idleAge >= 7.0)
{
spriteDy = 8;
frame = 1;
}
else if (idleAge >= 6.0)
{
spriteDy = 8;
frame = 0;
}
else if (idleAge >= 5.5f && facingDirection != 2)
{
facingDirection = 2;
idleAge -= 2.0f;
}
else if (fireAnimationDelay <= 0.0f && spellAnimationDelay <= 0.0f)
{
if (facingDirection == 6) spriteDy = 1;
else if (facingDirection == 8) spriteDy = 2;
else if (facingDirection == 4)
{
spriteDy = 1;
isMirroring = true;
}
}
else if (spellAnimationDelay >= 0.0f)
{
spriteDy = 7;
if (spellAnimationDelay < spellAnimationDelayMax * 0.1f) frame = 0;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.2f) frame = 1;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.7f) frame = 2;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.85f) frame = 1;
else frame = 0;
}
else
{
if (fireAnimationDirection == 2) spriteDy = 3;
else if (fireAnimationDirection == 6) spriteDy = 4;
else if (fireAnimationDirection == 8) spriteDy = 5;
else if (fireAnimationDirection == 4)
{
spriteDy = 4;
isMirroring = true;
}
if (fireAnimationDelay < fireAnimationDelayMax * 0.2f) frame = 0;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.4f) frame = 1;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.6f) frame = 2;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.8f) frame = 1;
else frame = 0;
}
if (playerStatus == playerStatusAcquire || playerStatus == playerStatusUnlocking)
{
spriteDy = 6;
frame = ((int)(age * 10.0f)) % 4;
if (frame == 3) frame = 1;
}
else if (playerStatus == playerStatusPraying)
{
spriteDy = 7;
frame = ((int)(age * 10.0f)) % 4;
if (frame == 3) frame = 1;
float delay = playerStatus == playerStatusPraying ? WORSHIP_DELAY : UNLOCK_DELAY;
if (statusTimer < delay * 0.1f) frame = 0;
else if (statusTimer < delay * 0.2f) frame = 1;
else if (statusTimer < delay * 0.7f) frame = 2;
else if (statusTimer < delay * 0.85f) frame = 1;
else frame = 0;
}
if (playerStatus == playerStatusDead)
{
frame = (int)(endAge / 0.35f);
if (frame > 6) frame = 6;
spriteDy = 9;
sprite.setTextureRect(sf::IntRect( frame * width, spriteDy * height, width, height));
app->draw(sprite);
}
else
{
if (isFairyTransmuted)
{
frame = 0;
if (velocity.x * velocity.x + velocity.y * velocity.y > 400)
frame = ((int)(age * 24.0f)) % 2;
else
frame = ((int)(age * 18.0f)) % 2;
sf::Sprite fairySprite;
fairySprite.setColor(sprite.getColor());
fairySprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
fairySprite.setPosition(sprite.getPosition());
fairySprite.setOrigin(26, 62);
switch (facingDirection)
{
case 8: fairySprite.setTextureRect(sf::IntRect( (2 + frame) * 48, 6 * 72, 48, 72)); break;
case 4: fairySprite.setTextureRect(sf::IntRect( (4 + frame) * 48, 6 * 72, 48, 72)); break;
case 6: fairySprite.setTextureRect(sf::IntRect( (5 + frame) * 48, 6 * 72, - 48, 72)); break;
default: fairySprite.setTextureRect(sf::IntRect( frame * 48, 6 * 72, 48, 72)); break;
}
app->draw(fairySprite);
}
else
{
renderHalo(app);
renderPlayer(app);
}
// shield
if (playerStatus != playerStatusStairs && (specialState[DivineStateProtection].active || protection.active))
{
int firstFrame = 8;
if (specialState[DivineStateProtection].active && divinity.divinity == DivinityStone) firstFrame = 16;
float timer = specialState[DivineStateProtection].active ? specialState[DivineStateProtection].timer : protection.timer;
sprite.setTextureRect(sf::IntRect( firstFrame * width, 9 * height, width, height));
app->draw(sprite);
sf::Color savedColor = sprite.getColor();
sprite.setColor(sf::Color(255, 255, 255, 100 + cos(age * (timer < 2.0f ? 25 : 10)) * 30 ));
sprite.setTextureRect(sf::IntRect( (firstFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
}
// divine field
if (divinity.level > 1)
{
bool displayField = false;
int fieldFrame;
int fieldFade;
switch (divinity.divinity)
{
case DivinityHealer:
{
displayField = true;
fieldFrame = 10;
fieldFade = 40 * (divinity.level - 1);
break;
}
case DivinityFighter:
{
displayField = true;
fieldFrame = 12;
fieldFade = 40 * (divinity.level - 1);
break;
}
case DivinityIce:
{
if (divinity.level > 2)
{
displayField = true;
fieldFrame = 14;
fieldFade = 80 * (divinity.level - 3);
}
break;
}
case DivinityStone:
{
if (divinity.level > 2)
{
displayField = true;
fieldFrame = 16;
fieldFade = 80 * (divinity.level - 3);
}
break;
}
case DivinityAir:
{
if (divinity.level > 1)
{
displayField = true;
fieldFrame = 18;
fieldFade = 40 * (divinity.level - 1);
}
break;
}
+ case DivinityIllusion:
+ {
+ if (divinity.level > 1)
+ {
+ displayField = true;
+ fieldFrame = 20;
+ fieldFade = 40 * (divinity.level - 1);
+ }
+ break;
+ }
+ case DivinityFire:
+ {
+ if (divinity.level > 1)
+ {
+ displayField = true;
+ fieldFrame = 22;
+ fieldFade = 40 * (divinity.level - 1);
+ }
+ break;
+ }
+ case DivinityDeath:
+ {
+ if (divinity.level > 1)
+ {
+ displayField = true;
+ fieldFrame = 24;
+ fieldFade = 40 * (divinity.level - 1);
+ }
+ break;
+ }
}
if (displayField)
{
sf::Color savedColor = sprite.getColor();
sprite.setColor(sf::Color(255, 255, 255, fieldFade ));
sprite.setTextureRect(sf::IntRect( fieldFrame * width, 9 * height, width, height));
app->draw(sprite);
if (divinity.divinity != DivinityStone && divinity.divinity != DivinityHealer)
{
sprite.setColor(sf::Color(255, 255, 255, 2 + fieldFade / 2 + cos(age * 15) * fieldFade / 2 ));
sprite.setTextureRect(sf::IntRect( (fieldFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
}
if (divinity.divinity == DivinityHealer && divineInterventionDelay > 0.0f && isRegeneration)
{
sprite.setTextureRect(sf::IntRect( (fieldFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
}
sprite.setColor(savedColor);
}
}
}
if (game().getShowLogical() && playerStatus != playerStatusDead)
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void PlayerEntity::calculateBB()
{
if (isFairyTransmuted)
{
boundingBox.left = (int)x - 10;
boundingBox.width = 20;
boundingBox.top = (int)y - 29;
boundingBox.height = 20;
}
else
{
boundingBox.left = (int)x - 10;
boundingBox.width = 20;
boundingBox.top = (int)y - 29;
boundingBox.height = 33;
}
}
void PlayerEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (playerStatus == playerStatusDead || !canCollide()) return;
EnemyBoltEntity* boltEntity = dynamic_cast<EnemyBoltEntity*>(entity);
if (collideWithEntity(entity))
{
if (boltEntity != NULL && !boltEntity->getDying())
{
- boltEntity->collide();
// TODO bolt source
- hurt(getHurtParams(boltEntity->getDamages(),
+ int boltResult = hurt(getHurtParams(boltEntity->getDamages(),
boltEntity->getBoltType(),
boltEntity->getLevel(),
boltEntity->isCritical(),
SourceTypeBolt,
boltEntity->getEnemyType(),
false));
- game().generateBlood(x, y, bloodColor);
- float xs = (x + boltEntity->getX()) / 2;
- float ys = (y + boltEntity->getY()) / 2;
- SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
- star->setFading(true);
- star->setZ(y+ 100);
- star->setLifetime(0.7f);
- star->setType(ENTITY_EFFECT);
- star->setSpin(400.0f);
+ if (boltResult > -1)
+ {
+ boltEntity->collide();
+
+ game().generateBlood(x, y, bloodColor);
+
+ float xs = (x + boltEntity->getX()) / 2;
+ float ys = (y + boltEntity->getY()) / 2;
+ SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
+ star->setFading(true);
+ star->setZ(y+ 100);
+ star->setLifetime(0.7f);
+ star->setType(ENTITY_EFFECT);
+ star->setSpin(400.0f);
+ }
}
}
}
bool PlayerEntity::willCollideWithMap(int dx, int dy, bool checkMiddle)
{
float oldX = x, oldY = y;
bool collide = true;
x = oldX + dx;
y = oldY + dy;
if (!isCollidingWithMap())
collide = false;
else if (checkMiddle)
{
x = oldX + dx / 2;
y = oldY + dy / 2;
if (!isCollidingWithMap())
collide = false;
}
x = oldX;
y = oldY;
return collide;
}
void PlayerEntity::move(int direction)
{
float oldX = x, oldY = y;
bool touchUp, touchDown, touchLeft, touchRight;
x = oldX + 1;
touchRight = isCollidingWithMap();
x = oldX - 1;
touchLeft = isCollidingWithMap();
x = oldX;
y = oldY + 1;
touchDown = isCollidingWithMap();
y = oldY - 1;
touchUp = isCollidingWithMap();
y = oldY;
if (specialState[SpecialStateConfused].active)
{
switch (direction)
{
case 4: direction = 6; break;
case 6: direction = 4; break;
case 2: direction = 8; break;
case 8: direction = 2; break;
case 1: direction = 9; break;
case 9: direction = 1; break;
case 7: direction = 3; break;
case 3: direction = 7; break;
}
}
keyDirection = direction;
if (playerStatus == playerStatusAcquire && statusTimer < ACQUIRE_DELAY / 2)
{
acquireItemAfterStance();
}
if (playerStatus == playerStatusPlaying)
{
collidingDirection = 5;
switch (keyDirection)
{
case 1:
if (touchDown && touchLeft)
{
direction = 5;
collidingDirection = 1;
}
else if (touchDown)
{
direction = 4;
collidingDirection = 2;
}
else if (touchLeft)
{
direction = 2;
collidingDirection = 4;
}
break;
case 3:
if (touchDown && touchRight)
{
direction = 5;
collidingDirection = 3;
}
else if (touchDown)
{
direction = 6;
collidingDirection = 2;
}
else if (touchRight)
{
direction = 2;
collidingDirection = 6;
}
break;
case 7:
if (touchUp && touchLeft)
{
direction = 5;
collidingDirection = 7;
}
else if (touchUp)
{
direction = 4;
collidingDirection = 8;
}
else if (touchLeft)
{
direction = 8;
collidingDirection = 1;
}
break;
case 9:
if (touchUp && touchRight)
{
direction = 5;
collidingDirection = 9;
}
else if (touchUp)
{
direction = 6;
collidingDirection = 8;
}
else if (touchRight)
{
direction = 8;
collidingDirection = 6;
}
break;
case 4:
if (touchLeft)
{
x = oldX - 2;
if (!willCollideWithMap(0, KEYS_MOVE_TOLERANCE, true))
direction = 2;
else if (!willCollideWithMap(0, -KEYS_MOVE_TOLERANCE, true))
direction = 8;
else
{
direction = 5;
collidingDirection = 4;
}
x = oldX;
}
break;
case 6:
if (touchRight)
{
x = oldX + 2;
if (!willCollideWithMap(0, KEYS_MOVE_TOLERANCE, true))
direction = 2;
else if (!willCollideWithMap(0, -KEYS_MOVE_TOLERANCE, true))
direction = 8;
else
{
direction = 5;
collidingDirection = 6;
}
x = oldX;
}
break;
case 8:
if (touchUp)
{
y = oldY - 2;
if (!willCollideWithMap(KEYS_MOVE_TOLERANCE, 0, true))
direction = 6;
else if (!willCollideWithMap(-KEYS_MOVE_TOLERANCE, 0, true))
direction = 4;
else
{
direction = 5;
collidingDirection = 8;
}
y = oldY;
}
break;
case 2:
if (touchDown)
{
y = oldY + 2;
if (!willCollideWithMap(KEYS_MOVE_TOLERANCE, 0, true))
direction = 6;
else if (!willCollideWithMap(-KEYS_MOVE_TOLERANCE, 0, true))
direction = 4;
else
{
direction = 5;
collidingDirection = 2;
}
y = oldY;
}
break;
}
float speedx = 0.0f, speedy = 0.0f;
if (direction == 4)
speedx = - creatureSpeed;
else if (direction == 1 || direction == 7)
speedx = - creatureSpeed * 0.7f;
else if (direction == 6)
speedx = creatureSpeed;
else if (direction == 3 || direction == 9)
speedx = creatureSpeed * 0.7f;
if (direction == 2)
speedy = creatureSpeed;
else if (direction == 1 || direction == 3)
speedy = creatureSpeed * 0.7f;
else if (direction == 8)
speedy = - creatureSpeed;
else if (direction == 7 || direction == 9)
speedy = - creatureSpeed * 0.7f;
setVelocity(Vector2D(speedx, speedy));
if (firingDirection != 5)
facingDirection = firingDirection;
else
{
switch (keyDirection)
{
case 8:
facingDirection = 8;
break;
case 2:
facingDirection = 2;
break;
case 4:
facingDirection = 4;
break;
case 6:
facingDirection = 6;
break;
case 7:
if (facingDirection != 4 && facingDirection != 8) facingDirection = 4;
break;
case 1:
if (facingDirection != 4 && facingDirection != 2) facingDirection = 4;
break;
case 9:
if (facingDirection != 6 && facingDirection != 8) facingDirection = 6;
break;
case 3:
if (facingDirection != 6 && facingDirection != 2) facingDirection = 6;
break;
}
}
}
}
bool PlayerEntity::isMoving()
{
if (velocity.x < -1.0f || velocity.x > 1.0f) return true;
if (velocity.y < -1.0f || velocity.y > 1.0f) return true;
return false;
}
bool PlayerEntity::isEquiped(int eq)
{
return equip[eq];
}
bool* PlayerEntity::getEquipment()
{
return equip;
}
void PlayerEntity::setEquipped(int item, bool toggleEquipped, bool isFairyPlayer)
{
equip[item] = toggleEquipped;
if (toggleEquipped && items[FirstEquipItem + item].familiar > FamiliarNone)
{
FairyEntity* fairy = new FairyEntity(x - 50.0f + rand() % 100,
y - 50.0f + rand() % 100,
items[FirstEquipItem + item].familiar,
isFairyPlayer);
fairies.push_back(fairy);
int nbFamiliarFairies = 0;
for (auto fairy : fairies) if (!fairy->isPlayerControlled()) nbFamiliarFairies++;
if (nbFamiliarFairies == 3) game().registerAchievement(AchievementFairies);
}
computePlayer();
}
void PlayerEntity::generateBolt(float velx, float vely)
{
enumShotType boltType = ShotTypeStandard;
unsigned int shotLevel = 1;
switch (getShotType())
{
case ShotTypeIce:
if (getShotType() == ShotTypeIce)
{
if (specialBoltTimer <= 0.0f)
{
boltType = ShotTypeIce;
shotLevel = getShotLevel();
needInitShotType = true;
}
else boltType = ShotTypeCold;
}
break;
case ShotTypeStandard:
case ShotTypeIllusion:
case ShotTypeStone:
case ShotTypeLightning:
case ShotTypeFire:
case ShotTypePoison:
boltType = getShotType();
shotLevel = getShotLevel();
break;
default:
std::cout << "[WARNING] Can not generate shot type: " << getShotType() << std::endl;
}
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, boltType, shotLevel);
bolt->setFlying(isFairyTransmuted);
- int boltDamage = fireDamages;
+ int boltDamage = fireDamage;
if (criticalChance > 0)
if (rand()% 100 < criticalChance)
{
boltDamage *= equip[EQUIP_CRITICAL_ADVANCED] ? 3 : 2;
bolt->setCritical(true);
}
bolt->setDamages(boltDamage);
if (equip[EQUIP_GLOVES_ADVANCED])
{
if (firingDirection == 2 || firingDirection == 8)
velx += velocity.x * 0.7f;
else if (firingDirection == 4 || firingDirection == 6)
vely += velocity.y * 0.7f;
}
bolt->setVelocity(Vector2D(velx, vely));
}
void PlayerEntity::rageFire(float damage, bool full, float velMult)
{
float tempFireVelocity = fireVelocity * velMult;
for (int i = -1; i <= 1; i += 2)
for (int j = -1; j <= 1; j += 2)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeFire, 0);
bolt->setDamages(10);
bolt->setFlying(isFairyTransmuted);
float velx = tempFireVelocity * i * 0.42f;
float vely = tempFireVelocity * j * 0.42f;
bolt->setVelocity(Vector2D(velx, vely));
if (full)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeFire, 0);
bolt->setDamages(10);
bolt->setFlying(isFairyTransmuted);
float velx = 0.0f;
float vely = 0.0f;
if (i == -1 && j == -1) velx = -tempFireVelocity * i * 0.6f;
else if (i == -1 && j == 1) velx = tempFireVelocity * i * 0.6f;
else if (i == 1 && j == -1) vely= -tempFireVelocity * i * 0.6f;
else if (i == 1 && j == 1) vely = tempFireVelocity * i * 0.6f;
bolt->setVelocity(Vector2D(velx, vely));
}
}
SoundManager::getInstance().playSound(SOUND_BLAST_FIRE);
}
void PlayerEntity::resetFireDirection()
{
firingDirection = 5;
}
int PlayerEntity::getFireDirection()
{
return firingDirection;
}
void PlayerEntity::fire(int direction)
{
if (age < 0.7f) return;
firingDirection = direction;
if (playerStatus != playerStatusDead)
for(int unsigned i = 0; i < fairies.size(); i++)
fairies[i]->fire(direction);
if (canFirePlayer && playerStatus != playerStatusDead && playerStatus != playerStatusAcquire)
{
canAnimateFire = true;
switch (getShotType())
{
case ShotTypeCold:
case ShotTypeIce:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_ICE);
break;
case ShotTypeFire:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_FIRE);
break;
case ShotTypeIllusion:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_ILLUSION);
break;
case ShotTypeLightning:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_LIGHTNING);
break;
case ShotTypePoison:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_POISON);
break;
case ShotTypeStone:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_STONE);
break;
default:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_STANDARD);
break;
}
if (equip[EQUIP_BOOK_DUAL] || equip[EQUIP_BOOK_TRIPLE]
|| equip[EQUIP_BOOK_DUAL_QUICK] || equip[EQUIP_BOOK_TRIPLE_QUICK])
{
float shoot_angle = 0.2f;
if ((direction == 4 && velocity.x < -1.0f) || (direction == 6 && velocity.x > 1.0f)
|| (direction == 8 && velocity.y < -1.0f) || (direction == 2 && velocity.y > 1.0f))
shoot_angle = 0.1f;
else if ((direction == 6 && velocity.x < -1.0f) || (direction == 4 && velocity.x > 1.0f)
|| (direction == 2 && velocity.y < -1.0f) || (direction == 8 && velocity.y > 1.0f))
shoot_angle = (equip[EQUIP_BOOK_TRIPLE] || equip[EQUIP_BOOK_TRIPLE_QUICK]) ? 0.35f : 0.2f;
else if (!equip[EQUIP_BOOK_TRIPLE] && !equip[EQUIP_BOOK_TRIPLE_QUICK])
shoot_angle = 0.1f;
switch(direction)
{
case 4:
generateBolt(-fireVelocity * cos(shoot_angle), fireVelocity * sin(shoot_angle));
generateBolt(-fireVelocity * cos(shoot_angle), - fireVelocity * sin(shoot_angle));
break;
case 6:
generateBolt(fireVelocity * cos(shoot_angle), fireVelocity * sin(shoot_angle));
generateBolt(fireVelocity * cos(shoot_angle), - fireVelocity * sin(shoot_angle));
break;
case 8:
generateBolt(fireVelocity * sin(shoot_angle), -fireVelocity * cos(shoot_angle));
generateBolt(-fireVelocity * sin(shoot_angle), - fireVelocity * cos(shoot_angle));
break;
case 2:
generateBolt(fireVelocity * sin(shoot_angle), fireVelocity * cos(shoot_angle));
generateBolt(-fireVelocity * sin(shoot_angle), fireVelocity * cos(shoot_angle));
break;
}
}
if (equip[EQUIP_RAPID_SHOT])
{
Vector2D boltDirection;
switch(direction)
{
case 4:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(-1, 0), fireVelocity, 0.2f);
break;
case 6:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(1, 0), fireVelocity, 0.2f);
break;
case 8:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(0, -1), fireVelocity, 0.2f);
break;
case 2:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(0, 1), fireVelocity, 0.2f);
break;
}
generateBolt(boltDirection.x, boltDirection.y);
}
else if (!(equip[EQUIP_BOOK_DUAL] || equip[EQUIP_BOOK_DUAL_QUICK]) || (equip[EQUIP_BOOK_TRIPLE] || equip[EQUIP_BOOK_TRIPLE_QUICK]))
{
switch(direction)
{
case 4:
generateBolt(-fireVelocity, 0.0f);
break;
case 6:
generateBolt(fireVelocity, 0.0f);
break;
case 8:
generateBolt(0.0f, -fireVelocity);
break;
case 2:
generateBolt(0.0f, fireVelocity);
break;
}
}
if (equip[EQUIP_REAR_SHOT_ADVANCED])
{
float shootAngle = 0.165f;
float boltVelocity = fireVelocity * 0.75f;
BoltEntity* bolt1 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt1->setDamages(fireDamages / 2);
+ bolt1->setDamages(fireDamage / 2);
bolt1->setFlying(isFairyTransmuted);
BoltEntity* bolt2 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt2->setDamages(fireDamages / 2);
+ bolt2->setDamages(fireDamage / 2);
bolt2->setFlying(isFairyTransmuted);
switch (direction)
{
case 4:
bolt1->setVelocity(Vector2D(boltVelocity * cos(shootAngle), boltVelocity * sin(shootAngle)));
bolt2->setVelocity(Vector2D(boltVelocity * cos(shootAngle), -boltVelocity * sin(shootAngle)));
break;
case 6:
bolt1->setVelocity(Vector2D(-boltVelocity * cos(shootAngle), boltVelocity * sin(shootAngle)));
bolt2->setVelocity(Vector2D(-boltVelocity * cos(shootAngle), -boltVelocity * sin(shootAngle)));
break;
case 2:
bolt1->setVelocity(Vector2D(boltVelocity * sin(shootAngle), -boltVelocity * cos(shootAngle)));
bolt2->setVelocity(Vector2D(-boltVelocity * sin(shootAngle), -boltVelocity * cos(shootAngle)));
break;
case 8:
bolt1->setVelocity(Vector2D(boltVelocity * sin(shootAngle), boltVelocity * cos(shootAngle)));
bolt2->setVelocity(Vector2D(-boltVelocity * sin(shootAngle), boltVelocity * cos(shootAngle)));
break;
}
}
else if (equip[EQUIP_REAR_SHOT])
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt->setDamages(fireDamages / 2);
+ bolt->setDamages(fireDamage / 2);
bolt->setFlying(isFairyTransmuted);
float velx = 0.0f;
float vely = 0.0f;
switch (direction)
{
case 4:
velx = fireVelocity * 0.75f;
break;
case 6:
velx = -fireVelocity * 0.75f;
break;
case 2:
vely = -fireVelocity * 0.75f;
break;
case 8:
vely = fireVelocity * 0.75f;
break;
}
bolt->setVelocity(Vector2D(velx, vely));
}
if (equip[EQUIP_SIDE_SHOTS])
{
BoltEntity* bolt1 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt1->setDamages(fireDamages / 2);
+ bolt1->setDamages(fireDamage / 2);
bolt1->setFlying(isFairyTransmuted);
BoltEntity* bolt2 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt2->setDamages(fireDamages / 2);
+ bolt2->setDamages(fireDamage / 2);
bolt2->setFlying(isFairyTransmuted);
if (direction == 4 || direction == 6)
{
bolt1->setVelocity(Vector2D(0.0f, fireVelocity * 0.75f));
bolt2->setVelocity(Vector2D(0.0f, -fireVelocity * 0.75f));
}
else
{
bolt1->setVelocity(Vector2D(fireVelocity * 0.75f, 0.0f));
bolt2->setVelocity(Vector2D(-fireVelocity * 0.75f, 0.0f));
}
}
if (equip[EQUIP_BOOK_RANDOM] && randomFireDelay <= 0.0f)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
- bolt->setDamages(fireDamages);
+ bolt->setDamages(fireDamage);
bolt->setFlying(isFairyTransmuted);
float shotAngle = rand() % 360;
bolt->setVelocity(Vector2D(fireVelocity * 0.75f * cos(shotAngle), fireVelocity * 0.75f * sin(shotAngle)));
randomFireDelay = fireDelay * 1.5f;
}
canFirePlayer = false;
currentFireDelay = fireDelay;
if (needInitShotType) initShotType();
}
else
{
canAnimateFire = false;
}
}
bool PlayerEntity::canMove()
{
return (playerStatus == playerStatusPlaying
|| (playerStatus == playerStatusAcquire && statusTimer < ACQUIRE_DELAY / 2));
}
int PlayerEntity::hurt(StructHurt hurtParam)
{
if (playerStatus == playerStatusDead) return false;
if (isFairyTransmuted && hurtParam.hurtingType != ShotTypeDeterministic)
{
hurtParam.damage *= 2;
}
shouldBeSavedFromDivinity = false;
bool divinityInvoked = false;
- if (hp - hurtParam.damage <= hpMax / 4 && divinity.divinity >= 0)
+ int thresholdDam = 5;
+
+ if (invincibleDelay <= 0.0f && hp - hurtParam.damage <= thresholdDam && divinity.divinity >= 0)
{
- divinityInvoked = triggerDivinityBefore();
- if (divinityInvoked)
+ if (triggerIllusionTeleport())
+ {
+ castTeleport();
+ lastTeleportSave = game().getGameTime();
+ divineInterventionDelay = WORSHIP_DELAY * 1.0f;
+ showCone = false;
+ return -999;
+ }
+ else if (game().getEnemyCount() > 2)
{
- game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivIntervention));
- shouldBeSavedFromDivinity = true;
+ divinityInvoked = triggerDivinityBefore();
+ if (divinityInvoked)
+ {
+ game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivIntervention));
+ shouldBeSavedFromDivinity = true;
+ }
}
}
if (invincibleDelay <= 0.0f || hurtParam.hurtingType == ShotTypeDeterministic)
{
SoundManager::getInstance().playSound(SOUND_PLAYER_HIT);
int oldHp = hp;
if (BaseCreatureEntity::hurt(hurtParam) > 0)
{
if (hurtParam.hurtingType != ShotTypeDeterministic)
{
invincibleDelay = INVINCIBLE_DELAY;
if (equip[EQUIP_AMULET_RETALIATION] && !equip[EQUIP_RAGE_AMULET]) rageFire(10, hp <= hpMax / 5, 1.0f);
game().generateBlood(x, y, bloodColor);
}
hurtingDelay = HURTING_DELAY * 2.0f;
game().generateBlood(x, y, bloodColor);
game().proceedEvent(EventBeingHurted);
if (oldHp > hp && game().getLevel() <= LAST_LEVEL)
lostHp[game().getLevel() - 1] += (oldHp - hp);
lastHurtingEnemy = hurtParam.enemyType;
lastHurtingSource = hurtParam.sourceType;
// divinity
offerHealth(oldHp - hp);
- if (!divinityInvoked && hp <= hpMax / 4 && divinity.divinity >= 0)
+ if (!divinityInvoked && hp <= thresholdDam && divinity.divinity >= 0)
{
triggerDivinityAfter();
}
game().addHurtingStat(oldHp - hp);
return true;
}
}
return false;
}
void PlayerEntity::setMap(GameMap* map, int tileWidth, int tileHeight, int offsetX, int offsetY)
{
CollidingSpriteEntity::setMap(map, tileWidth, tileHeight, offsetX, offsetY);
//if (slimePet != NULL) slimePet->setMap(map, tileWidth, tileHeight, offsetX, offsetY);
}
void PlayerEntity::loseItem(enumItemType itemType, bool isEquip)
{
CollidingSpriteEntity* itemSprite
= new CollidingSpriteEntity(ImageManager::getInstance().getImage(isEquip ? IMAGE_ITEMS_EQUIP : IMAGE_ITEMS), x, y, 32, 32);
itemSprite->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
itemSprite->setZ(-1);
itemSprite->setFrame(itemType);
itemSprite->setImagesProLine(10);
itemSprite->setType(ENTITY_BLOOD);
itemSprite->setVelocity(Vector2D(200 + rand()%450));
itemSprite->setViscosity(0.95f);
itemSprite->setSpin( (rand() % 700) - 350.0f);
}
void PlayerEntity::dying()
{
if (shouldBeSavedFromDivinity)
{
hp = 33 * hpMax / 100;
return;
}
else if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
hp = 1;
return;
}
playerStatus = playerStatusDead;
endAge = 0.0f;
hp = 0;
SoundManager::getInstance().playSound(SOUND_PLAYER_DIE);
setVelocity(Vector2D(0.0f, 0.0f));
int i;
for (i = 0; i < gold && i < 10; i++) loseItem(ItemCopperCoin, false);
for (i = 0; i < 5; i++) game().generateBlood(x, y, BloodRed);
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
if (equip[i])
{
if (items[i + FirstEquipItem].familiar == FamiliarNone)
loseItem(enumItemType(i), true);
}
}
remove(SAVE_FILE.c_str());
if (game().getLevel() == 1) game().registerAchievement(AchievementNoob);
game().calculateScore();
}
void PlayerEntity::displayAcquiredGold(int n)
{
std::ostringstream oss;
if (n > 0) oss << "+";
oss << n;
TextEntity* text = new TextEntity(oss.str(), 16, x, y - 30.0f);
text->setColor(TextEntity::COLOR_FADING_YELLOW);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
}
void PlayerEntity::acquireItem(enumItemType type)
{
if (items[type].generatesStance) acquireStance(type);
else if (items[type].consumable)
{
acquireConsumable(type);
}
else switch (type)
{
case ItemCopperCoin:
gold++;
displayAcquiredGold(1);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemSilverCoin:
gold = gold + 5;
displayAcquiredGold(5);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemGoldCoin:
gold = gold + 10;
displayAcquiredGold(10);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemHealthVerySmallPoison:
specialState[SpecialStatePoison].active = false;
case ItemHealthVerySmall:
heal(equip[EQUIP_MANUAL_HEALTH] ? 5 : 3);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
case ItemHealthSmall:
heal(equip[EQUIP_MANUAL_HEALTH] ? 10 : 7);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
case ItemHealth:
heal(equip[EQUIP_MANUAL_HEALTH] ? 22 : 15);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
default:
break;
}
}
bool isUnidentified(enumItemType item)
{
return (item >= ItemPotion01 && item < ItemPotion01 + NUMBER_UNIDENTIFIED);
}
int PlayerEntity::getConsumable(int n)
{
if (n < 0 || n >= MAX_SLOT_CONSUMABLES) return -1;
else return consumable[n];
}
void PlayerEntity::setConsumable(int n, int type)
{
if (n < 0 || n >= MAX_SLOT_CONSUMABLES) return;
consumable[n] = type;
}
void PlayerEntity::dropConsumables(int n)
{
if (n < 0 || n >= MAX_SLOT_CONSUMABLES) return;
if (playerStatus != playerStatusPlaying) return;
if (consumable[n] < 0) return;
ItemEntity* newItem = new ItemEntity((enumItemType)(consumable[n]), x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
newItem->setAge(-5.0f);
newItem->startsJumping();
consumable[n] = -1;
}
void PlayerEntity::tryToConsume(int n)
{
if (n < 0 || n >= MAX_SLOT_CONSUMABLES) return;
if (playerStatus != playerStatusPlaying) return;
if (consumable[n] > -1)
{
// unidentified
if (isUnidentified((enumItemType)consumable[n]))
{
enumItemType potion = (enumItemType)consumable[n];
enumItemType potionEffect = game().getPotion(potion);
game().setPotionToKnown(potion);
for (int i = 0; i < MAX_SLOT_CONSUMABLES; i++)
if (consumable[i] == potion) consumable[i] = potionEffect;
consume(potionEffect);
}
else if (items[consumable[n]].consumable)
// known
{
consume((enumItemType)consumable[n]);
}
else
{
std::cout << "[ERROR] Trying to consume item: " << items[consumable[n]].name << std::endl;
}
}
consumable[n] = -1;
}
void PlayerEntity::consume(enumItemType item)
{
switch(item)
{
case ItemScrollRevelation:
reveal();
spellAnimationDelay = spellAnimationDelayMax;
SoundManager::getInstance().playSound(SOUND_SCROLL);
break;
case ItemPotionHealth:
heal(equip[EQUIP_MANUAL_HEALTH] ? 28 : 18);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionPoison:
specialState[SpecialStatePoison].active = true;
specialState[SpecialStatePoison].timer = POISON_TIMER[0];
specialState[SpecialStatePoison].param1 = POISON_DAMAGE[0];
specialState[SpecialStatePoison].param2 = POISON_DELAY[0];
specialState[SpecialStatePoison].param3 = POISON_DELAY[0];
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("poison"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionSpeed:
specialState[SpecialStateSpeed].active = false;
specialState[SpecialStateSpeed].waitUnclear = true;
specialState[SpecialStateSpeed].timer = 30;
specialState[SpecialStateSpeed].param1 = 1.5f;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_speed"), TextEntity::COLOR_FADING_BLUE);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionSlow:
specialState[SpecialStateSlow].active = false;
specialState[SpecialStateSlow].waitUnclear = true;
specialState[SpecialStateSlow].timer = 30;
specialState[SpecialStateSlow].param1 = 0.35f;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_slow"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionWeakness:
specialState[SpecialStateWeakness].active = false;
specialState[SpecialStateWeakness].waitUnclear = true;
specialState[SpecialStateWeakness].timer = 30;
specialState[SpecialStateWeakness].param1 = 0.5f;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_weakness"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionStrength:
specialState[SpecialStateStrength].active = false;
specialState[SpecialStateStrength].waitUnclear = true;
specialState[SpecialStateStrength].timer = 30;
specialState[SpecialStateStrength].param1 = 1.5f;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_strength"), TextEntity::COLOR_FADING_BLUE);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionOblivion:
game().forget();
if (!equip[EQUIP_BOOK_ALCHEMY])
{
for (int i = 0; i < MAX_SLOT_CONSUMABLES; i++)
{
if (consumable[i] > -1)
{
if (consumable[i] >= ItemPotion01 + NUMBER_UNIDENTIFIED && consumable[i] < FirstEquipItem )
{
consumable[i] = game().getPotion((enumItemType)consumable[i]);
}
}
}
}
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_forget"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionConfusion:
specialState[SpecialStateConfused].active = false;
specialState[SpecialStateConfused].waitUnclear = true;
specialState[SpecialStateConfused].timer = 10;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_confusion"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionCure:
for (int i = 0; i < DivineStateProtection; i++)
{
specialState[i].active = false;
specialState[i].waitUnclear = false;
}
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_cure"), TextEntity::COLOR_FADING_BLUE);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
case ItemPotionRage:
specialState[SpecialStateRage].active = false;
specialState[SpecialStateRage].waitUnclear = true;
specialState[SpecialStateRage].timer = 20.5f;
specialState[SpecialStateRage].param1 = 12;
specialState[SpecialStateRage].param2 = 2;
specialState[SpecialStateRage].param3 = 2;
displayFlyingText( x, y - 20.0f, 16, tools::getLabel("effect_rage"), TextEntity::COLOR_FADING_RED);
SoundManager::getInstance().playSound(SOUND_DRINK);
break;
default:
std::cout << "[ERROR] Trying to consume item: " << items[item].name << std::endl;
break;
}
SpriteEntity* usedItem = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_ITEMS), x, y, 32, 32);
usedItem->setImagesProLine(10);
if (item == ItemScrollRevelation)
usedItem->setFrame((int)item);
else
usedItem->setFrame(38);
usedItem->setFading(true);
usedItem->setZ(y - 100);
usedItem->setLifetime(0.7f);
usedItem->setAge(-0.7);
usedItem->setType(ENTITY_EFFECT);
usedItem->setSpin(rand() % 400 - 200);
usedItem->setVelocity(Vector2D(60));
}
void PlayerEntity::reveal()
{
for (int i = 0; i < MAX_SLOT_CONSUMABLES; i++)
{
if (consumable[i] > -1)
{
if (isUnidentified((enumItemType)consumable[i]))
{
game().setPotionToKnown((enumItemType)consumable[i]);
consumable[i] = game().getPotion((enumItemType)consumable[i]);
}
}
}
if (game().getCurrentMap()->callRevelation())
{
game().refreshMinimap();
SoundManager::getInstance().playSound(SOUND_SECRET);
}
}
bool PlayerEntity::canAquireConsumable(enumItemType type)
{
int nbConsumableSlot = equip[EQUIP_BAG] ? 4 : 2;
for (int i = 0; i < nbConsumableSlot; i++)
{
if (consumable[i] <= 0) return true;
}
return false;
}
void PlayerEntity::acquireConsumable(enumItemType type)
{
int nbConsumableSlot = equip[EQUIP_BAG] ? 4 : 2;
int emptySlot = -1;
for (int i = 0; emptySlot == -1 && i < nbConsumableSlot; i++)
{
if (consumable[i] <= 0) emptySlot = i;
}
if (emptySlot > -1)
{
consumable[emptySlot] = type;
if (type == ItemScrollRevelation)
SoundManager::getInstance().playSound(SOUND_SCROLL);
else
SoundManager::getInstance().playSound(SOUND_BOTTLE);
// events
game().proceedEvent(EventConsumable);
if (isUnidentified(type)) game().proceedEvent(EventPotion);
}
}
void PlayerEntity::onClearRoom()
{
if (divinity.divinity == DivinityHealer)
{
if (divinity.level > 1 && hp < hpMax)
{
divineInterventionDelay = WORSHIP_DELAY / 2;
isRegeneration = true;
showCone = true;
if (divinity.level >= 5) heal(4);
else if (divinity.level >= 4) heal(3);
else if (divinity.level >= 3) heal(2);
else if (divinity.level >= 2) heal(1);
}
}
}
void PlayerEntity::computePlayer()
{
float boltLifeTimeBonus = 1.0f;
float fireDelayBonus = 1.0f;
float creatureSpeedBonus = 1.0f;
float fireVelocityBonus = 1.0f;
- float fireDamagesBonus = 1.0f;
+ float fireDamageBonus = 1.0f;
armor = 0.0f;
criticalChance = 0;
for (int i = 0; i < NB_RESISTANCES; i++) resistance[i] = ResistanceStandard;
// gloves
if (equip[EQUIP_GLOVES_ADVANCED]) fireDelayBonus -= 0.15f;
else if (equip[EQUIP_DISPLACEMENT_GLOVES]) fireDelayBonus -= 0.10f;
// hat
if (equip[EQUIP_HAT_ADVANCED])
{
fireDelayBonus -= 0.3f;
resistance[ResistanceIce] = (enumStateResistance)(resistance[ResistanceIce] - 1);
resistance[ResistanceStone] = (enumStateResistance)(resistance[ResistanceStone] - 1);
resistance[ResistanceLightning] = (enumStateResistance)(resistance[ResistanceLightning] - 1);
resistance[ResistanceFire] = (enumStateResistance)(resistance[ResistanceFire] - 1);
}
else if (equip[EQUIP_MAGICIAN_HAT]) fireDelayBonus -= 0.2f;
// belt
if (equip[EQUIP_LEATHER_BELT]) fireDelayBonus -= 0.15f;
// boots
if (equip[EQUIP_BOOTS_ADVANCED]) creatureSpeedBonus += 0.25f;
else if (equip[EQUIP_LEATHER_BOOTS]) creatureSpeedBonus += 0.15f;
// multi-fire
if (equip[EQUIP_BOOK_TRIPLE]) fireDelayBonus += 0.7f;
else if (equip[EQUIP_BOOK_DUAL]) fireDelayBonus += 0.5f;
if (equip[EQUIP_CRITICAL]) criticalChance += 5;
if (equip[EQUIP_MANUAL_STAFF]) boltLifeTimeBonus += 0.4f;
if (equip[EQUIP_MAHOGANY_STAFF])
{
fireVelocityBonus += 0.15f;
- fireDamagesBonus += 0.5f;
+ fireDamageBonus += 0.5f;
}
- if (equip[EQUIP_BLOOD_SNAKE]) fireDamagesBonus += 0.5f;
+ if (equip[EQUIP_BLOOD_SNAKE]) fireDamageBonus += 0.5f;
if (equip[EQUIP_ROBE_ADVANCED]) armor += 0.2f;
else if (equip[EQUIP_MAGICIAN_ROBE]) armor += 0.15f;
// divinity
switch (divinity.divinity)
{
case (DivinityHealer):
{
break;
}
case (DivinityFighter):
{
if (divinity.level >= 5)
- fireDamagesBonus += 0.5f;
+ fireDamageBonus += 0.5f;
else if (divinity.level >= 4)
- fireDamagesBonus += 0.375f;
+ fireDamageBonus += 0.375f;
else if (divinity.level >= 3)
- fireDamagesBonus += 0.25f;
+ fireDamageBonus += 0.25f;
else if (divinity.level >= 2)
- fireDamagesBonus += 0.125f;
+ fireDamageBonus += 0.125f;
break;
}
case (DivinityIce):
{
if (divinity.level >= 5) resistance[ResistanceFrozen] = ResistanceVeryHigh;
if (divinity.level >= 3) resistance[ResistanceIce] = (enumStateResistance)(resistance[ResistanceIce] - 1);
break;
}
+ case (DivinityFire):
+ {
+ if (divinity.level >= 3) resistance[ResistanceFire] = (enumStateResistance)(resistance[ResistanceFire] - 1);
+ break;
+ }
+ case (DivinityDeath):
+ {
+ if (divinity.level >= 5) resistance[ResistancePoison] = ResistanceVeryHigh;
+ else if (divinity.level >= 3) resistance[ResistancePoison] = ResistanceHigh;
+ break;
+ }
case (DivinityStone):
{
- if (divinity.level >= 5) resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ if (divinity.level >= 5) resistance[ResistanceRepulsion] = ResistanceVeryHigh;
if (divinity.level >= 3) resistance[ResistanceStone] = (enumStateResistance)(resistance[ResistanceStone] - 1);
break;
}
case (DivinityAir):
{
if (divinity.level > 1) creatureSpeedBonus += (divinity.level - 1) * 0.04f;
if (divinity.level >= 3) resistance[ResistanceLightning] = (enumStateResistance)(resistance[ResistanceLightning] - 1);
break;
}
}
fireDelay = INITIAL_PLAYER_FIRE_DELAY * fireDelayBonus;
creatureSpeed = INITIAL_PLAYER_SPEED * creatureSpeedBonus;
fireVelocity = INITIAL_BOLT_VELOCITY * fireVelocityBonus;
- fireDamages = INITIAL_BOLT_DAMAGES * fireDamagesBonus;
+ fireDamage = INITIAL_BOLT_DAMAGES * fireDamageBonus;
boltLifeTime = INITIAL_BOLT_LIFE * boltLifeTimeBonus;
// gems
for (int i = 1; i < SPECIAL_SHOT_SLOTS; i++)
{
specialShotLevel[i] = 0;
switch (specialShots[i])
{
case ShotTypeIce:
if (equip[EQUIP_RING_ICE]) specialShotLevel[i]++;
if (divinity.divinity == DivinityIce && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeStone:
if (equip[EQUIP_RING_STONE]) specialShotLevel[i]++;
if (divinity.divinity == DivinityStone && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeLightning:
if (equip[EQUIP_RING_LIGHTNING]) specialShotLevel[i]++;
if (divinity.divinity == DivinityAir && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeIllusion:
if (equip[EQUIP_RING_ILLUSION]) specialShotLevel[i]++;
+ if (divinity.divinity == DivinityIllusion && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeFire:
if (equip[EQUIP_RING_FIRE]) specialShotLevel[i]++;
+ if (divinity.divinity == DivinityFire && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypePoison:
if (equip[EQUIP_RING_POISON]) specialShotLevel[i]++;
+ if (divinity.divinity == DivinityDeath && divinity.level >= 4) specialShotLevel[i]++;
break;
default:
break;
}
}
- if (getShotType() == ShotTypeIllusion) fireDamages *= ILLUSION_DAMAGE_DECREASE[getShotLevel()];
- else if (getShotType() == ShotTypeFire) fireDamages *= FIRE_DAMAGE_INCREASE[getShotLevel()];
+ if (getShotType() == ShotTypeIllusion) fireDamage *= ILLUSION_DAMAGE_DECREASE[getShotLevel()];
+ else if (getShotType() == ShotTypeFire) fireDamage *= FIRE_DAMAGE_INCREASE[getShotLevel()];
// divinity
if (specialState[DivineStateProtection].active)
armor += specialState[DivineStateProtection].param1;
// post-computation
- if (equip[EQUIP_BOOK_TRIPLE_QUICK]) fireDamages *= 0.65f;
- else if (equip[EQUIP_BOOK_DUAL_QUICK]) fireDamages *= 0.75f;
+ if (equip[EQUIP_BOOK_TRIPLE_QUICK]) fireDamage *= 0.65f;
+ else if (equip[EQUIP_BOOK_DUAL_QUICK]) fireDamage *= 0.75f;
else if (equip[EQUIP_RAPID_SHOT])
{
fireDelay *= 0.20f;
- fireDamages *= 0.25f;
+ fireDamage *= 0.25f;
}
- if (equip[EQUIP_ALCOHOL]) fireDamages *= 1.25f;
+ if (equip[EQUIP_ALCOHOL]) fireDamage *= 1.25f;
// spells
if (protection.active) armor += protection.value;
if (armor > 1.0f) armor = 1.0f;
// fairy ?
if (isFairyTransmuted)
{
- fireDamages *= 0.5f;
+ fireDamage *= 0.5f;
creatureSpeed *= 1.5f;
movingStyle = movFlying;
}
else
{
movingStyle = movWalking;
}
// potions
if (specialState[SpecialStateWeakness].active && !specialState[SpecialStateStrength].active)
- fireDamages *= specialState[SpecialStateWeakness].param1;
+ fireDamage *= specialState[SpecialStateWeakness].param1;
if (specialState[SpecialStateStrength].active && !specialState[SpecialStateWeakness].active)
- fireDamages *= specialState[SpecialStateStrength].param1;
+ fireDamage *= specialState[SpecialStateStrength].param1;
+
+ // Damage Pro Second
+ dps = fireDamage / fireDelay;
+ if (equip[EQUIP_BOOK_TRIPLE] || equip[EQUIP_BOOK_TRIPLE_QUICK]) dps *= 3;
+ else if (equip[EQUIP_BOOK_DUAL] || equip[EQUIP_BOOK_DUAL_QUICK]) dps *= 2;
}
void PlayerEntity::acquireStance(enumItemType type)
{
velocity.x = 0.0f;
velocity.y = 0.0f;
playerStatus = playerStatusAcquire;
statusTimer = ACQUIRE_DELAY;
acquiredItem = (enumItemType)(type);
SoundManager::getInstance().playSound(SOUND_BONUS);
game().showArtefactDescription(type);
enumItemType itemFrame = type;
int itemImage = IMAGE_ITEMS;
if (itemFrame >= FirstEquipItem)
{
itemFrame = (enumItemType)(itemFrame - FirstEquipItem);
itemImage = IMAGE_ITEMS_EQUIP;
}
spriteItem = new SpriteEntity(
ImageManager::getInstance().getImage(itemImage),
x, y - 100.0f, ITEM_WIDTH, ITEM_HEIGHT);
spriteItem->setFrame((int)itemFrame);
spriteItem->setImagesProLine(10);
spriteItem->setZ(z);
spriteItem->setLifetime(ACQUIRE_DELAY);
spriteItemStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_STAR),
x, y - 100.0f);
spriteItemStar->setScale(4.0f, 4.0f);
spriteItemStar->setZ(z-1.0f);
spriteItemStar->setLifetime(ACQUIRE_DELAY);
spriteItemStar->setSpin(50.0f);
}
void PlayerEntity::collideMapRight()
{
collidingDirection = 6;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x * 0.7f;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x * 0.7f;
}
void PlayerEntity::collideMapLeft()
{
collidingDirection = 4;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x * 0.7f;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x * 0.7f;
}
void PlayerEntity::collideMapTop()
{
collidingDirection = 8;
- if (recoil.active) recoil.velocity.y= -recoil.velocity.y * 0.7f;
+ if (repulsion.active) repulsion.velocity.y= -repulsion.velocity.y * 0.7f;
}
void PlayerEntity::collideMapBottom()
{
collidingDirection = 2;
- if (recoil.active) recoil.velocity.y= -recoil.velocity.y * 0.7f;
+ if (repulsion.active) repulsion.velocity.y= -repulsion.velocity.y * 0.7f;
}
void PlayerEntity::useBossKey()
{
velocity.x = 0.0f;
velocity.y = 0.0f;
playerStatus = playerStatusUnlocking;
statusTimer = UNLOCK_DELAY;
acquiredItem = (enumItemType)(type - FirstEquipItem);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING_BOSS);
equip[EQUIP_BOSS_KEY] = false;
SpriteEntity* spriteItem = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP),
x, y - 80.0f, ITEM_WIDTH, ITEM_HEIGHT);
spriteItem->setFrame(EQUIP_BOSS_KEY);
spriteItem->setZ(z);
spriteItem->setAge(-UNLOCK_DELAY * 0.5f);
spriteItem->setLifetime(UNLOCK_DELAY * 0.5f);
spriteItem->setFading(true);
spriteItem->setSpin(300);
}
enumShotType PlayerEntity::getShotType()
{
return specialShots[specialShotIndex];
}
int PlayerEntity::getShotIndex()
{
return specialShotIndex;
}
void PlayerEntity::setShotIndex(int index)
{
specialShotIndex = index;
}
enumShotType PlayerEntity::getShotType(int slot)
{
return specialShots[slot];
}
void PlayerEntity::setShotType(int slot, enumShotType shotType)
{
specialShots[slot] = shotType;
}
void PlayerEntity::registerSpecialShot(int item)
{
bool found = false;
int index = 1;
while (index < SPECIAL_SHOT_SLOTS && !found)
{
found = specialShots[index] == ShotTypeStandard;
if (!found) index++;
}
if (found)
{
this->specialShots[index] = items[item].specialShot;
specialShotIndex = index;
initShotType();
}
}
void PlayerEntity::selectShotType(int n)
{
if (n == specialShotIndex) return;
if (n > 0 && getShotType(n) == ShotTypeStandard) return;
specialShotIndex = n;
initShotType();
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
computePlayer();
}
void PlayerEntity::selectNextShotType()
{
int index = specialShotIndex + 1;
bool found = false;
while (index < SPECIAL_SHOT_SLOTS && !found)
{
if (specialShots[index] == ShotTypeStandard) index++;
else found = true;
}
if (found)
{
specialShotIndex = index;
initShotType();
}
else
specialShotIndex = 0;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
computePlayer();
}
void PlayerEntity::initShotType()
{
specialBoltTimer = STATUS_FROZEN_BOLT_DELAY[getShotLevel()];
needInitShotType = false;
if (getShotType() == ShotTypeLightning)
SoundManager::getInstance().playSound(SOUND_ELECTRIC_CHARGE);
}
unsigned int PlayerEntity::getShotLevel()
{
return specialShotLevel[specialShotIndex];
}
unsigned int PlayerEntity::getShotLevel(int index)
{
return specialShotLevel[index];
}
int PlayerEntity::getFairieNumber()
{
return fairies.size();
}
FairyEntity* PlayerEntity::getFairy(unsigned int n)
{
if (n < fairies.size())
return fairies[n];
else
return NULL;
}
bool PlayerEntity::canGetNewShot(bool advancedShot)
{
int nbSpecial =0;
int nbAdvanced =0;
for (int i = 1; i < SPECIAL_SHOT_SLOTS; i++)
{
switch (specialShots[i])
{
case ShotTypeIce:
case ShotTypeStone:
case ShotTypeLightning:
nbSpecial++;
break;
case ShotTypeFire:
case ShotTypeIllusion:
case ShotTypePoison:
nbAdvanced++;
break;
case ShotTypeStandard:
break;
default:
std::cout << "[WARNING] Can not register shot type: " << getShotType() << std::endl;
}
}
if (advancedShot)
return (nbAdvanced >= SPECIAL_SHOT_SLOTS_ADVANCED);
else
return (nbSpecial >= SPECIAL_SHOT_SLOTS_STANDARD);
}
void PlayerEntity::interact(EnumInteractionType interaction, int id)
{
if (playerStatus == playerStatusPlaying)
{
// praying at the temple
if (interaction == InteractionTypeTemple)
{
if (divinity.divinity == id)
{
// donation
if (gold >= 10)
{
donate(10);
}
}
else
{
worship((enumDivinityType)id);
}
}
else if (interaction == InteractionTypeMerchandise)
{
if (itemToBuy != NULL) itemToBuy->buy();
}
}
}
float PlayerEntity::getBolPositionY()
{
if (isFairyTransmuted)
return y - 25;
else
return y - 20;
}
bool PlayerEntity::collideWithMap(int direction)
{
if (playerStatus == playerStatusEntering)
return false;
else
return BaseCreatureEntity::collideWithMap(direction);
}
// DIVINITY
void PlayerEntity::donate(int n)
{
if (gold >= n)
{
gold -= n;
donation += n;
if (donation >= 100) game().registerAchievement(AchievementFanatic);
displayAcquiredGold(-n);
SoundManager::getInstance().playSound(SOUND_PAY);
// standard : 1 gold = 3 piety
int pietyProGold = 3;
if (divinity.divinity == DivinityHealer)
pietyProGold = 5;
addPiety(pietyProGold * n);
// check item invoke
bool divineGift = false;
enumItemType itemType = ItemCopperCoin;
+ if (divinity.level >= 4 && game().getItemsCount() == 0 && donation >= 40)
+ {
+ if (divinity.divinity == DivinityIce && equip[EQUIP_GEM_ICE] && !equip[EQUIP_RING_ICE])
+ {
+ divineGift = true;
+ itemType = ItemRingIce;
+ }
+ else if (divinity.divinity == DivinityStone && equip[EQUIP_GEM_STONE] && !equip[EQUIP_RING_STONE])
+ {
+ divineGift = true;
+ itemType = ItemRingStone;
+ }
+ else if (divinity.divinity == DivinityAir && equip[EQUIP_GEM_LIGHTNING] && !equip[EQUIP_RING_LIGHTNING])
+ {
+ divineGift = true;
+ itemType = ItemRingLightning;
+ }
+ else if (divinity.divinity == DivinityIllusion && equip[EQUIP_GEM_ILLUSION] && !equip[EQUIP_RING_ILLUSION])
+ {
+ divineGift = true;
+ itemType = ItemRingIllusion;
+ }
+ else if (divinity.divinity == DivinityFire && equip[EQUIP_GEM_FIRE] && !equip[EQUIP_RING_FIRE])
+ {
+ divineGift = true;
+ itemType = ItemRingFire;
+ }
+ else if (divinity.divinity == DivinityDeath && equip[EQUIP_GEM_POISON] && !equip[EQUIP_RING_POISON])
+ {
+ divineGift = true;
+ itemType = ItemRingPoison;
+ }
+ }
+
if (divinity.level >= 3 && game().getItemsCount() == 0)
{
if (divinity.divinity == DivinityHealer && !equip[EQUIP_MANUAL_HEALTH])
{
// Healer + level 3 = Health manual
divineGift = true;
itemType = ItemManualHealth;
}
else if (divinity.divinity == DivinityIce && !equip[EQUIP_GEM_ICE])
{
divineGift = true;
itemType = ItemGemIce;
}
else if (divinity.divinity == DivinityStone && !equip[EQUIP_GEM_STONE])
{
divineGift = true;
itemType = ItemGemStone;
}
else if (divinity.divinity == DivinityAir && !equip[EQUIP_GEM_LIGHTNING])
{
divineGift = true;
itemType = ItemGemLightning;
}
+ else if (divinity.divinity == DivinityIllusion && !equip[EQUIP_GEM_ILLUSION])
+ {
+ divineGift = true;
+ itemType = ItemGemIllusion;
+ }
+ else if (divinity.divinity == DivinityFire && !equip[EQUIP_GEM_FIRE])
+ {
+ divineGift = true;
+ itemType = ItemGemFire;
+ }
+ else if (divinity.divinity == DivinityDeath && !equip[EQUIP_GEM_POISON])
+ {
+ divineGift = true;
+ itemType = ItemGemPoison;
+ }
}
if (divineGift)
{
float xItem = GAME_WIDTH / 2;
float yItem = GAME_HEIGHT * 0.8f;
new ItemEntity(itemType, xItem, yItem);
SoundManager::getInstance().playSound(SOUND_OM);
divineInterventionDelay = WORSHIP_DELAY / 2;
showCone = true;
isRegeneration = false;
for (int i = 0; i < 8; i++)
{
game().generateStar(sf::Color::White, xItem, yItem);
game().generateStar(sf::Color(255, 255, 210), xItem, yItem);
}
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivGift));
}
}
}
void PlayerEntity::offerMonster(enemyTypeEnum monster, enumShotType hurtingType)
{
if (divinity.divinity > -1)
{
// standard : 1 monster = 2 piety - 1 boss = 20 piety
int pietyProMonster = 2;
int pietyProBoss = 20;
switch (divinity.divinity)
{
case DivinityHealer:
if (monster == EnemyTypeGhost
|| monster == EnemyTypeZombie
|| monster == EnemyTypeZombieDark
|| monster == EnemyTypeImpBlue
|| monster == EnemyTypeImpRed
|| monster == EnemyTypeWitch
|| monster == EnemyTypeWitchRed
|| monster == EnemyTypeBogeyman)
pietyProMonster = 4;
else
pietyProMonster = 0;
break;
case DivinityFighter:
pietyProMonster = 3;
pietyProBoss = 30;
break;
case DivinityIce:
if (monster == EnemyTypeSlimeRed
|| monster == EnemyTypeImpRed
|| monster == EnemyTypeEvilFlowerFire)
pietyProMonster = 4;
if (hurtingType == ShotTypeCold || hurtingType == ShotTypeIce)
{
pietyProMonster *= 1.5f;
pietyProBoss = 25;
}
break;
case DivinityStone:
- if (hurtingType == ShotTypeCold || hurtingType == ShotTypeIce)
+ if (hurtingType == ShotTypeStone)
{
pietyProMonster = 3;
pietyProBoss = 30;
}
else
{
pietyProBoss = 25;
}
break;
- }
+ case DivinityIllusion:
+ if (hurtingType == ShotTypeIllusion)
+ {
+ pietyProMonster = 3;
+ pietyProBoss = 30;
+ }
+ else
+ {
+ pietyProBoss = 25;
+ }
+ break;
+ }
if (monster < EnemyTypeButcher) // normal or mini-boss
{
addPiety(pietyProMonster);
}
else if (monster < EnemyTypeBat_invocated) // boss
{
addPiety(pietyProBoss);
}
}
}
void PlayerEntity::offerHealth(int lostHp)
{
if (divinity.divinity == DivinityHealer)
{
addPiety(lostHp * 2.5f);
}
-
}
void PlayerEntity::offerChallenge()
{
if (divinity.divinity >= 0)
addPiety(30);
}
+void PlayerEntity::offerSecret()
+{
+ if (divinity.divinity == DivinityIllusion)
+ addPiety(35);
+ else
+ addPiety(10);
+}
+
void PlayerEntity::divineFury()
{
enumShotType shotType = ShotTypeStandard;
if (divinity.divinity == DivinityIce) shotType = ShotTypeIce;
else if (divinity.divinity == DivinityStone) shotType = ShotTypeStone;
else if (divinity.divinity == DivinityAir) shotType = ShotTypeLightning;
+ else if (divinity.divinity == DivinityIllusion) shotType = ShotTypeIllusion;
+ else if (divinity.divinity == DivinityFire) shotType = ShotTypeFire;
+ else if (divinity.divinity == DivinityDeath) shotType = ShotTypePoison;
int multBonus = 6;
- if (divinity.divinity == DivinityFighter) multBonus = 8;
+ if (divinity.divinity == DivinityFighter || divinity.divinity == DivinityFire) multBonus = 8;
for (int i = 0; i < (divinity.divinity == DivinityAir ? 16 : 32); i ++)
{
BoltEntity* bolt = new BoltEntity(TILE_WIDTH * 1.5f + rand() % (MAP_WIDTH - 3) * TILE_WIDTH ,
TILE_HEIGHT * 1.5f + rand() % (MAP_HEIGHT - 3) * TILE_HEIGHT,
boltLifeTime, shotType, 0);
bolt->setDamages(8 + divinity.level * multBonus);
float velx = 400 * cos(i);
float vely = 400 * sin(i);
bolt->setVelocity(Vector2D(velx, vely));
if (divinity.divinity == DivinityAir)
{
bolt->setFlying(true);
bolt->setLifetime(10.0f);
}
else
{
bolt->setLifetime(8.0f);
}
bolt->setViscosity(1.0f);
bolt->setGoThrough(true);
bolt->setFromPlayer(false);
}
}
void PlayerEntity::divineDestroyUndead()
{
game().destroyUndead(40);
}
void PlayerEntity::divineIce()
{
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
enemy->setSpecialState(SpecialStateIce, true, 10.0f, 0.1f, 0.0f);
}
}
}
+void PlayerEntity::divineFire()
+{
+ game().makeShake(1.0f);
+ SoundManager::getInstance().playSound(SOUND_BOOM_00);
+
+ for (int i = 0; i < 15; i++)
+ {
+ int xRand = 48 + rand() % (GAME_WIDTH - 96);
+ int yRand = 48 + rand() % (GAME_HEIGHT - 96);
+
+ new ExplosionEntity(xRand, yRand, ExplosionTypeStandard, 16, EnemyTypeNone, false);
+ }
+}
+
void PlayerEntity::divineRepulse()
{
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
enemy->hurt(getHurtParams
(8,
ShotTypeStandard,
0,
false,
SourceTypeBolt,
EnemyTypeNone,
false));
- enemy->giveRecoil(true, Vector2D(x, y).vectorTo(Vector2D(enemy->getX(), enemy->getY()), 700.0f), 2.0f);
+ enemy->giveRepulsion(true, Vector2D(x, y).vectorTo(Vector2D(enemy->getX(), enemy->getY()), 700.0f), 2.0f);
}
}
// effect
for (int i = 0; i < 40; i++)
{
SpriteEntity* spriteRock = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_CYCLOP),
x, y, 64, 64);
spriteRock->setZ(1000.0f);
spriteRock->setImagesProLine(20);
spriteRock->setFrame(rand() % 2 == 0 ? 38 : 58);
spriteRock->setSpin(-100 + rand()%200);
spriteRock->setVelocity(Vector2D(400 + rand()%400));
spriteRock->setFading(true);
spriteRock->setAge(-0.8f);
spriteRock->setLifetime(2.0f);
spriteRock->setType(ENTITY_EFFECT);
}
game().makeShake(1.0f);
SoundManager::getInstance().playSound(SOUND_EARTHQUAKE);
}
void PlayerEntity::divineProtection(float duration, float armorBonus)
{
setSpecialState(DivineStateProtection, true, duration, armorBonus, 0.0f);
}
void PlayerEntity::divineHeal(int hpHealed)
{
int oldHp = hp;
hp += hpHealed;
if (hp > hpMax) hp = hpMax;
specialState[SpecialStatePoison].active = false;
divineInterventionDelay = WORSHIP_DELAY;
showCone = true;
isRegeneration = false;
game().addHealingStat(hp - oldHp);
}
+bool PlayerEntity::triggerIllusionTeleport()
+{
+ if (divinity.divinity == DivinityIllusion)
+ {
+ // TODO timer
+ if (game().getGameTime() - lastTeleportSave > 15) return true;
+ }
+ return false;
+}
+
bool PlayerEntity::triggerDivinityBefore()
{
if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
switch (divinity.divinity)
{
case DivinityHealer:
{
if (game().getUndeadCount() > 0 && rand() % 2 == 0)
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 2);
divineDestroyUndead();
game().makeColorEffect(X_GAME_COLOR_WHITE, 0.45f);
return true;
}
break;
}
case DivinityFighter:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 1) divineProtection(8.0f, 0.8f);
else divineFury();
game().makeColorEffect(X_GAME_COLOR_RED, 0.45f);
return true;
break;
}
case DivinityIce:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 1)
{
divineIce();
game().makeColorEffect(X_GAME_COLOR_BLUE, 7.5f);
}
else
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.5f);
}
return true;
break;
}
case DivinityStone:
{
int r = rand() % 2;
divineProtection(10.0f, 0.75f);
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 0)
{
divineRepulse();
game().makeColorEffect(X_GAME_COLOR_BROWN, 3.0f);
}
else
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_BROWN, 0.5f);
}
return true;
break;
}
case DivinityAir:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
- /*if (r == 1)
{
- divineIce();
- game().makeColorEffect(X_GAME_COLOR_BLUE, 7.5f);
+ divineFury();
+ game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.5f);
}
- else*/
+ return true;
+ break;
+ }
+ case DivinityIllusion:
+ {
+ int r = rand() % 3;
+ if (r == 0) return false;
+
+ SoundManager::getInstance().playSound(SOUND_OM);
+ incrementDivInterventions();
+ divineHeal(hpMax / 3);
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.5f);
}
return true;
break;
}
+ case DivinityFire:
+ {
+ int r = rand() % 3;
+ if (r == 0) return false;
+
+ SoundManager::getInstance().playSound(SOUND_OM);
+ incrementDivInterventions();
+ divineHeal(hpMax / 3);
+ if (r == 1)
+ {
+ divineFire();
+ game().makeColorEffect(X_GAME_COLOR_RED, 1.0f);
+ }
+ else
+ {
+ divineFury();
+ game().makeColorEffect(X_GAME_COLOR_RED, 0.5f);
+ }
+
+ return true;
+ break;
+ }
+ case DivinityDeath:
+ {
+ int r = rand() % 3;
+ if (r == 0) return false;
+
+ SoundManager::getInstance().playSound(SOUND_OM);
+ incrementDivInterventions();
+ divineHeal(hpMax / 3);
+ {
+ divineFury();
+ game().makeColorEffect(X_GAME_COLOR_GREEN, 0.5f);
+ }
+ return true;
+ break;
+ }
}
}
return false;
}
void PlayerEntity::triggerDivinityAfter()
{
if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
switch (divinity.divinity)
{
case DivinityHealer:
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax);
break;
}
- //case DivinityFighter:
default:
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 2);
break;
}
}
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivIntervention));
}
}
void PlayerEntity::addPiety(int n)
{
if (n > 0 && equip[EQUIP_BOOK_PRAYER_I]) n *= 1.5f;
int oldLevel = divinity.level;
divinity.piety += n;
if (divinity.piety >= DIVINITY_LEVEL_TRESHOLD[MAX_DIVINITY_LEVEL - 1])
{
divinity.piety = DIVINITY_LEVEL_TRESHOLD[MAX_DIVINITY_LEVEL - 1];
divinity.percentsToNextLevels = 1.0f;
game().registerAchievement(AchievementPietyMax);
divinity.level = MAX_DIVINITY_LEVEL + 1;
}
else
{
int i = 0;
while (divinity.piety > DIVINITY_LEVEL_TRESHOLD[i] && i < (MAX_DIVINITY_LEVEL + 1)) i++;
divinity.level = i + 1;
if (divinity.level == 1)
divinity.percentsToNextLevels = (float)divinity.piety / (float)DIVINITY_LEVEL_TRESHOLD[0];
else
divinity.percentsToNextLevels
= (float)(divinity.piety - DIVINITY_LEVEL_TRESHOLD[divinity.level - 2])
/ (float)(DIVINITY_LEVEL_TRESHOLD[divinity.level - 1] - DIVINITY_LEVEL_TRESHOLD[divinity.level - 2]);
}
if (divinity.level > oldLevel)
{
SoundManager::getInstance().playSound(SOUND_OM);
divineInterventionDelay = WORSHIP_DELAY * 1.5f;
showCone = false;
isRegeneration = false;
pietyLevelUp();
computePlayer();
}
else if (divinity.level < oldLevel)
{
computePlayer();
}
}
void PlayerEntity::pietyLevelUp()
{
std::string label = "";
switch (divinity.divinity)
{
case DivinityFighter:
label = "div_fighter_lvl";
break;
case DivinityHealer:
if (divinity.level == 2) label = "div_healer_lvl_2";
else label = "div_healer_lvl_3";
break;
case DivinityIce:
if (divinity.level == 3) label = "div_ice_lvl_3";
else if (divinity.level == 4) label = "div_ice_lvl_4";
else if (divinity.level == 5) label = "div_ice_lvl_5";
break;
case DivinityStone:
if (divinity.level == 3) label = "div_stone_lvl_3";
else if (divinity.level == 4) label = "div_stone_lvl_4";
else if (divinity.level == 5) label = "div_stone_lvl_5";
break;
case DivinityAir:
if (divinity.level == 4) label = "div_air_lvl_4";
else label = "div_air_lvl";
break;
+
+ case DivinityIllusion:
+ if (divinity.level == 3) label = "div_illusion_lvl_3";
+ else if (divinity.level == 4) label = "div_illusion_lvl_4";
+ break;
+
}
if (label.compare("") != 0) game().addDivLevelMessageToQueue(label);
}
void PlayerEntity::incrementDivInterventions()
{
divinity.interventions++;
addPiety(-divinity.piety * (equip[EQUIP_BOOK_PRAYER_II] ? 0.04f : 0.08f));
}
void PlayerEntity::worship(enumDivinityType id)
{
int oldPiety = divinity.piety;
int oldLevel = divinity.level;
bool isReconversion = divinity.divinity > -1;
playerStatus = playerStatusPraying;
statusTimer = WORSHIP_DELAY;
SoundManager::getInstance().playSound(SOUND_OM);
divinity.divinity = id;
divinity.piety = 0;
divinity.level = 1;
divinity.percentsToNextLevels = 0.0f;
facingDirection = 2;
// text
float x0 = MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = MAP_HEIGHT * 0.5f * TILE_HEIGHT + 140.0f;
std::stringstream ss;
ss << tools::getLabel("worshipping") << " ";
ss << tools::getLabel(divinityLabel[divinity.divinity] + "_0");
TextEntity* text = new TextEntity(ss.str(), 24, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1200);
text->setColor(TextEntity::COLOR_FADING_WHITE);
// reconversion
if (isReconversion)
{
if (oldLevel >= 4)
game().registerAchievement(AchievementApostate);
addPiety((equip[EQUIP_BOOK_PRAYER_I]) ? 0.66 * oldPiety : 0.5 * oldPiety);
if (divinity.interventions > divinity.level - 1)
divinity.interventions = divinity.level - 1;
}
else
divinity.interventions = 0;
// message
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivHealer + (int)id));
}
void PlayerEntity::loadDivinity(int id, int piety, int level, int interventions)
{
divinity.divinity = id;
divinity.piety = piety;
divinity.level = level;
divinity.interventions = interventions;
if (id >= 0) addPiety(0);
}
// MAGIC
castSpellStruct PlayerEntity::getActiveSpell()
{
return activeSpell;
}
void PlayerEntity::setActiveSpell(enumCastSpell spell, bool fromSaveInFight)
{
if (activeSpell.spell != SpellNone)
{
// drop the old spell
equip[activeSpell.frame] = false;
ItemEntity* newItem = new ItemEntity((enumItemType)(ItemMagicianHat + activeSpell.frame), x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
}
activeSpell.spell = spell;
switch (spell)
{
case SpellTeleport:
activeSpell.delayMax = 20.0f;
activeSpell.frame = ItemSpellTeleport - FirstEquipItem;
break;
case SpellSlimeExplode:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellSlimeExplode - FirstEquipItem;
break;
case SpellFireball:
activeSpell.delayMax = 20.0f;
activeSpell.frame = ItemSpellFireball - FirstEquipItem;
break;
case SpellFreeze:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellFreeze - FirstEquipItem;
break;
case SpellEarthquake:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellEarthquake - FirstEquipItem;
break;
case SpellProtection:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellProtection - FirstEquipItem;
break;
case SpellWeb:
activeSpell.delayMax = 35.0f;
activeSpell.frame = ItemSpellWeb - FirstEquipItem;
break;
case SpellFlower:
activeSpell.delayMax = 80.0f;
activeSpell.frame = ItemSpellFlower - FirstEquipItem;
break;
case SpellFairy:
activeSpell.delayMax = 10.0f;
activeSpell.frame = ItemSpellFairy - FirstEquipItem;
break;
case SpellTime:
activeSpell.delayMax = 70.0f;
activeSpell.frame = ItemSpellTime - FirstEquipItem;
break;
case SpellLightning:
activeSpell.delayMax = 70.0f;
activeSpell.frame = ItemSpellLightning - FirstEquipItem;
break;
case SpellNone:
break;
}
if (fromSaveInFight) activeSpell.delay = 1.0f;
else activeSpell.delay = activeSpell.delayMax;
}
void PlayerEntity::castSpell()
{
if (playerStatus != playerStatusPlaying) return;
if (canCastSpell())
{
activeSpell.delay = equip[EQUIP_BOOK_MAGIC_I] ? activeSpell.delayMax * 0.8f : activeSpell.delayMax;
switch (activeSpell.spell)
{
case SpellTeleport:
castTeleport();
break;
case SpellSlimeExplode:
castSummonsSlimeExplode();
break;
case SpellFireball:
castFireball();
break;
case SpellFreeze:
spellAnimationDelay = spellAnimationDelayMax;
castFreeze();
break;
case SpellEarthquake:
spellAnimationDelay = spellAnimationDelayMax;
castEarthquake();
break;
case SpellProtection:
spellAnimationDelay = spellAnimationDelayMax;
castProtection();
break;
case SpellWeb:
castWeb();
break;
case SpellFlower:
spellAnimationDelay = spellAnimationDelayMax;
castSummonsFlower();
break;
case SpellFairy:
castTransmuteFairy();
break;
case SpellTime:
spellAnimationDelay = spellAnimationDelayMax;
castTimeStop();
break;
case SpellLightning:
spellAnimationDelay = spellAnimationDelayMax;
castLightning();
break;
case SpellNone:
break;
}
}
}
bool PlayerEntity::canCastSpell()
{
return activeSpell.spell != SpellNone && activeSpell.delay <= 0.0f;
}
void PlayerEntity::castTeleport()
{
bool ok = false;
int xm, ym;
float xNew = x, yNew = y;
invincibleDelay = equip[EQUIP_BOOK_MAGIC_II] ? 2.5f : 2.0f;
SoundManager::getInstance().playSound(SOUND_TELEPORT);
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.3f);
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
int counter = 150;
while (!ok && counter > 0)
{
counter--;
int distanceMin = 20000;
if (counter < 50) distanceMin = 30000;
else if (counter < 100) distanceMin = 25000;
xm = 1 +rand() % (MAP_WIDTH - 3);
ym = 1 +rand() % (MAP_HEIGHT - 3);
if (game().getCurrentMap()->isWalkable(xm, ym))
{
// enemy or bolt ?
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
bool isBad = false;
xNew = xm * TILE_WIDTH + TILE_WIDTH * 0.5f;
yNew = ym * TILE_HEIGHT+ TILE_HEIGHT * 0.5f;
for (it = entityList->begin (); !isBad && it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if ((e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT) || e->getType() == ENTITY_ENEMY_BOLT)
isBad = Vector2D(xNew, yNew).distance2(Vector2D(e->getX(), e->getY())) < distanceMin;
}
if (!isBad)
{
x = xNew;
y = yNew;
}
}
}
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
void PlayerEntity::initFallingGrid()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
fallingGrid[i][j] = false;
}
void PlayerEntity::fallRock()
{
int rx, ry;
do
{
rx = 1 + rand() % (MAP_WIDTH - 2);
ry = 1 + rand() % (MAP_HEIGHT - 2);
}
while (fallingGrid[rx][ry]);
fallingGrid[rx][ry] = true;
new FallingRockEntity(rx * TILE_WIDTH + TILE_WIDTH / 2,
ry * TILE_HEIGHT + TILE_HEIGHT / 2,
rand() % 3,
true);
}
void PlayerEntity::castSummonsSlimeExplode()
{
SlimeEntity* slime = new SlimeEntity( ((int)(x) / TILE_WIDTH) * TILE_WIDTH + TILE_WIDTH * 0.5f,
y - 5, SlimeTypeViolet, true);
slime->makePet(facingDirection);
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.3f);
}
void PlayerEntity::castFireball()
{
SoundManager::getInstance().playSound(SOUND_FIREBALL);
game().makeColorEffect(X_GAME_COLOR_RED, 0.3f);
enumShotType boltType = ShotTypeFire;
unsigned int shotLevel = 2;
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime + 0.5f, boltType, shotLevel);
- int boltDamage = fireDamages * (equip[EQUIP_BOOK_MAGIC_II] ? 4 : 3);
- if (equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 32) boltDamage = 32;
- else if (!equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 24) boltDamage = 24;
+ int boltDamage = fireDamage * (equip[EQUIP_BOOK_MAGIC_II] ? 6 : 4);
+ if (equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 44) boltDamage = 44;
+ else if (!equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 32) boltDamage = 32;
bolt->setDamages(boltDamage);
bolt->setGoThrough(true);
float velx = 0.0f, vely = 0.0f;
if (facingDirection == 4) velx = -fireVelocity;
else if (facingDirection == 8) vely = -fireVelocity;
else if (facingDirection == 2) vely = fireVelocity;
else velx = fireVelocity;
bolt->setVelocity(Vector2D(velx, vely));
}
void PlayerEntity::castFreeze()
{
int iceLevel = equip[EQUIP_BOOK_MAGIC_II] ? 2 : 1;
for (float i = 0.0f; i < 2 * PI; i += PI / 8)
{
BoltEntity* bolt1 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeIce, iceLevel);
bolt1->setDamages(1);
float velx = fireVelocity * cos(i);
float vely = fireVelocity * sin(i);
bolt1->setVelocity(Vector2D(velx, vely));
}
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.3f);
SoundManager::getInstance().playSound(SOUND_SPELL_FREEZE);
}
void PlayerEntity::castEarthquake()
{
initFallingGrid();
int nbIterations = equip[EQUIP_BOOK_MAGIC_II] ? 24 : 22;
for (int i = 0; i < nbIterations; i++) fallRock();
game().makeShake(0.25f);
game().makeColorEffect(X_GAME_COLOR_BROWN, 0.3f);
SoundManager::getInstance().playSound(SOUND_EARTHQUAKE);
}
void PlayerEntity::castProtection()
{
protection.active = true;
protection.value = equip[EQUIP_BOOK_MAGIC_II] ? 0.6f : 0.4f;
protection.timer = 10.0f;
computePlayer();
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.3f);
SoundManager::getInstance().playSound(SOUND_SPELL_SHIELD);
}
void PlayerEntity::castWeb()
{
SoundManager::getInstance().playSound(SOUND_SPIDER_WEB);
int nbWeb = equip[EQUIP_BOOK_MAGIC_II] ? 4 : 3;
for (int i = 0; i < nbWeb; i++)
{
SpiderWebEntity* web = new SpiderWebEntity(x, y, true);
float webVel = 100 + rand()% 500;
float webAngle = -60 + rand() % 120;
webAngle = PI * webAngle / 180.0f;
if (facingDirection == 4) webAngle += PI;
else if (facingDirection == 8) webAngle -= PI * 0.5;
else if (facingDirection == 2) webAngle += PI * 0.5;
web->setVelocity(Vector2D(webVel * cos(webAngle), webVel * sin(webAngle)));
}
}
void PlayerEntity::castSummonsFlower()
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
EvilFlowerEntity* flower = new EvilFlowerEntity(x, y, FlowerTypePet);
flower->setLifetime(equip[EQUIP_BOOK_MAGIC_II] ? 45 : 35);
if (equip[EQUIP_BOOK_MAGIC_II]) flower->setFireDelayMax(EVIL_FLOWER_FIRE_DELAY * 0.8f);
}
void PlayerEntity::castTransmuteFairy()
{
if (isFairyTransmuted)
{
movingStyle = movWalking;
if (isCollidingWithMap())
movingStyle = movFlying;
else
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
isFairyTransmuted = false;
computePlayer();
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
}
else
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
isFairyTransmuted = true;
computePlayer();
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
}
void PlayerEntity::castTimeStop()
{
specialState[SpecialStateTime].active = true;
specialState[SpecialStateTime].timer = equip[EQUIP_BOOK_MAGIC_II] ? 7 : 5;
game().pauseMusic();
}
void PlayerEntity::castLightning()
{
game().makeColorEffect(X_GAME_COLOR_WHITE, 0.4f);
int nbBolts = equip[EQUIP_BOOK_MAGIC_II] ? 9 : 7;
for (int i = 0; i < nbBolts ; i++)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeLightning, 0);
int boltDamage = game().getLevel() + 2;
if (i == 0) boltDamage += 10;
bolt->setDamages(boltDamage);
float shotAngle = rand() % 360;
bolt->setVelocity(Vector2D(400 * cos(shotAngle), 400 * sin(shotAngle)));
bolt->setViscosity(1.0f);
bolt->setLifetime(5 + 0.1f * (float)(rand() % 50));
}
SpriteEntity* lightningSprite = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_LIGHTNING), x, y - 300);
lightningSprite->setFading(true);
lightningSprite->setLifetime(0.2f);
lightningSprite->setAge(-0.4f);
lightningSprite->setRenderAdd();
lightningSprite->setZ(2000);
SoundManager::getInstance().playSound(SOUND_THUNDER);
}
+
+bool PlayerEntity::seeInvisible()
+{
+ return (divinity.divinity == DivinityIllusion && divinity.level >= 3);
+}
diff --git a/src/PlayerEntity.h b/src/PlayerEntity.h
index 61ad317..9b7dbb4 100644
--- a/src/PlayerEntity.h
+++ b/src/PlayerEntity.h
@@ -1,647 +1,656 @@
#ifndef PLAYERSPRITE_H
#define PLAYERSPRITE_H
#include "BaseCreatureEntity.h"
#include "ItemEntity.h"
#include "Constants.h"
class FairyEntity;
struct castSpellStruct
{
enumCastSpell spell;
float delay;
float delayMax;
int frame;
};
struct divinityStruct
{
int divinity;
int piety;
int level;
int interventions;
float percentsToNextLevels;
};
const int MAX_SLOT_CONSUMABLES = 4;
/*! \class PlayerEntity
* \brief Class for the player
*
* It contains the game logic of the player and the rendering.
*/
class PlayerEntity : public BaseCreatureEntity
{
public:
/*!
* \brief Constructor
*
* Constructor of the PlayerEntity class.
*
* \param x : x position of the player
* \param y : y position of the player
*/
PlayerEntity(float x, float y);
/*!
* \brief updates the player
*
* Updates the player.
* Called in the game loop.
*
* \param delay : elapsed time since the last call
*/
virtual void animate(float delay);
/*!
* \brief render the player
*
* Render the player.
* Called in the game loop.
*
* \param app : Rendering target
*/
virtual void render(sf::RenderTarget* app);
/*!
* \brief Moves the player to another place
*
* Moves the player to another place.
* Called when changing room, the "familiers" will move too.
*
* \param newX : target x position of the player
* \param newY : target y position of the player
*/
void moveTo(float newX, float newY);
/*!
* \brief returns the direction the player is facing
*
* Return the direction the player is facing.
* Use to know in which direction he has to fire with the "one button" gameplay.
*
* \return : the facing direction. 4 = left, 8 = north, 6 = right, 2 = south
*/
int getFacingDirection();
void setFacingDirection(int facingDirection);
/*!
* \brief update the bounding box
*
* Update the bounding box of the player.
* Used before testing collision.
*/
virtual void calculateBB();
virtual bool canCollide();
/*!
* \brief Moves the player in the given direction
*
* Moves the player in the given direction.
*
* \param direction : direction of the new map. Numeric pad, diagonals included : 4 = left, 8 = north, 7 = north-west...
*/
void move(int direction);
/*!
* \brief Fires in the given direction
*
* Fires the player in the given direction.
*
* \param direction : direction of the new map. 4 = left, 8 = north, 6 = right, 2 = south
*/
void fire(int direction);
/*!
* \brief reset the fire direction of the player
*/
void resetFireDirection();
/*!
* \brief accessor on the fire direction of the player
*/
int getFireDirection();
/*!
* \brief returns if the player is moving or not
*
* Returns if the player is moving or not.
*
* \return : True if the player is moving
*/
bool isMoving();
/*!
* \brief returns if an item is equipped or not
*
* Returns if an item is equipped or not.
*
* \param eq : the equip item ID
* \return : True if the item is in possession of the player
*/
bool isEquiped(int eq);
bool* getEquipment();
/*!
* \brief returns if the player is poisoned or not
*
* \return : True if the player is poisoned
*/
bool isPoisoned();
/*!
* \brief updates the equipment of the player
*
* Updates the equipment of the player.
*
* \param item : the equip item ID
* \param toggleEquipped : True if the item has to be equipped
*/
void setEquipped(int item, bool toggleEquipped, bool isFairyPlayer = false);
divinityStruct getDivinity();
int getPiety();
bool getFairyTransmuted();
/*!
* \brief updates the entering status of the player
*
* Updates the entering status of the player.
* Used when the player is entering in a not yet cleared room.
*/
void setEntering();
/*!
* \brief updates the status of the player to going up
*
* Updates the status of the player to going up.
* Used when the player is leaving a level.
*/
void setLeavingLevel();
float getFireRate();
/*!
* \brief returns if the player can move or not
*
* Returns if the player can move or not.
*
* \return : True if the player can move
*/
bool canMove();
/*!
* \brief called when the player is dying
*
* Called when the player is dying (HP <= 0).
*/
virtual void dying();
/*!
* \brief hurts the player
*
* Hurts the player.
* Calld when the player is hurt, usually when colliding with a monster or a missile.
*
* \param damages : the inflicted damages
* \param hurtingType : damages type
* \param level : damages level
* \return : True if the player has been hurt
*/
virtual int hurt(StructHurt hurtParam) override;
/*!
* \brief returns if the player is dead or not
*
* Returns if the player is dead or not.
*
* \return : True if the player is dead
*/
bool isDead();
/*!
* \brief returns the fire delay percentage
*
* Returns the fire delay percentage.
* Used (currently) when displaying the blue bar in the HUD.
*
* \return : the percentage (between 0.0f for empty to 1.0 for full)
*/
float getPercentFireDelay();
/*!
* \brief returns the light cone percentage
*
* \return : the percentage (between 0.0f for empty to 1.0 for full) or -1.0f if no light cone
*/
float getFadingDivinity(bool showCone);
/*!
* \brief called when the player get an item
*
* Called when the player get an item.
* When the item is an equip item, he starts an "acquiring stance".
*
* \param type : item ID
*/
void acquireItem(enumItemType type);
/*!
* \brief called when the player get an item and ends the stance
*
*/
void acquireItemAfterStance();
/*!
* \brief makes the player drop an item
*
* Makes the player drop an item.
* Called when the player dies.
*
* \param itemType : item ID
* \param isEquip : True if it's an equip item (not the same texture)
*/
void loseItem(enumItemType itemType, bool isEquip);
/*!
* \brief starts the acquire stance
*
* Starts the acquire stance.
* Called when the player get an equip object. The item is "highlighted" and a description is displayed.
*
* \param type : item ID
*/
void acquireStance(enumItemType type);
/*!
* \brief starts the opening stance
*
* Starts the boss door opening animation.
* Remove the key from the inventory.
*/
void useBossKey();
int getLostHp(int level);
void setLostHp(int level, int n);
/*!
* \brief accessor on the gold
*
* Accessor on the gold.
*
* \return : the gold
*/
int getGold() {return gold; }
/*!
* \brief mutator on the gold
*
* Mutator on the gold.
*
* \param gold : the new gold value
*/
void setGold(int gold) { this->gold = gold; }
/*!
* \brief accessor on the donation
*
* Accessor on the donation.
*
* \return : the donation
*/
int getDonation() {return donation; }
/*!
* \brief mutator on the donation
*
* Mutator on the donation.
*
* \param gold : the new donation value
*/
void setDonation(int gold) { this->donation = gold; }
/*!
* \brief pay some gold
*
* Pay some gold. Usually in shops.
*
* \param gold : the price to pay
*/
void pay(int price);
void displayAcquiredGold(int n);
/** Player status enum
* The different player states.
*/
enum playerStatusEnum
{
playerStatusPlaying, /**< Player is playing "normally" */
playerStatusEntering, /**< Player is entering a not yet cleared room (walking is forced) */
playerStatusAcquire, /**< Player is under acquiring stance */
playerStatusUnlocking, /**< Player is under unlocking stance */
playerStatusPraying, /**< Player is under unlocking stance */
playerStatusStairs, /**< Player walk the stairs */
playerStatusGoingNext, /**< Player goes to next level */
playerStatusDead, /**< Player RIP */
playerStatusVictorious /**< Game won */
};
/*!
* \brief accessor on the player status
*
* Accessor on the player status.
*
* \return : the player status
*/
playerStatusEnum getPlayerStatus();
/*!
* \brief mutator on the player status
*
* Mutator on the player status.
*
* \param player status : the new player status value
*/
void setPlayerStatus(playerStatusEnum playerStatus);
/*!
* \brief accessor on the colliding direction
*
* Accessor on the colliding direction.
*
* \return : the colliding direction
*/
int getCollidingDirection();
/*!
* \brief compute all the player data (stats / items)
*/
void computePlayer();
/*!
* \brief register a new special shot and select it
*
* \param item : Item which provides the shot
*/
void registerSpecialShot(int item);
/*!
* \brief accessor on current shot type
*
* \return : the current shot type
*/
enumShotType getShotType();
/*!
* \brief accessor on current shot level
*
* \return : the current shot level
*/
unsigned int getShotLevel();
/*!
* \brief accessor a shot level
* \param index : the index of the shot
* \return : the shot level
*/
unsigned int getShotLevel(int index);
/*!
* \brief accessor on current shot index
*
* Accessor on the current shot index.
*
* \return : the current shot index
*/
int getShotIndex();
/*!
* \brief mutator on the shot index
*
* Mutator on the shot index.
*
* \param index : the new shot index
*/
void setShotIndex(int index);
/*!
* \brief accessor on current shot type of a slot
*
* \param slot : The slot number
* \return : the current shot type
*/
enumShotType getShotType(int slot);
/*!
* \brief mutator on a shot slot
*
* Mutator on a shot slot.
*
* \param index : the shot index
* \param shotType : the shot type to set
*/
void setShotType(int slot, enumShotType shotType);
/*!
* \brief select the next shot type
*
* Select the next shot type.
*/
void selectNextShotType();
void selectShotType(int n);
/*!
* \brief accessor on the fairies number
*/
int getFairieNumber();
/*!
* \brief accessor on a fairy
* \param n : index of the fairy in the vector
*/
FairyEntity* getFairy(unsigned int n);
/*!
* \brief checks if the player can get a new shot type
*
* \param advancedShot : true if the shot type is "advanced"
* \return : true if there is a free slot
*/
bool canGetNewShot(bool advancedShot);
float getEndAge();
void setEndAge(float endAge);
virtual void setMap(GameMap* map, int tileWidth, int tileHeight, int offsetX, int offsetY) override;
/*!
* \brief casts a spell (if possible)
*/
void castSpell();
void onClearRoom();
void resetFloorItem();
void interact(EnumInteractionType interaction, int id);
void worship(enumDivinityType divinity);
void donate(int n);
void offerMonster(enemyTypeEnum monster, enumShotType hurtingType);
void offerHealth(int lostHp);
void offerChallenge();
+ void offerSecret();
void addPiety(int n);
void pietyLevelUp();
void loadDivinity(int id, int piety, int level, int interventions);
+ bool triggerIllusionTeleport();
bool triggerDivinityBefore();
void triggerDivinityAfter();
void divineFury();
void divineDestroyUndead();
void divineIce();
void divineRepulse();
+ void divineFire();
void divineProtection(float duration, float armorBonus);
void divineHeal(int hpHealed);
void setActiveSpell(enumCastSpell spell, bool fromSaveInFight);
castSpellStruct getActiveSpell();
float getPercentSpellDelay();
bool canCastSpell();
enemyTypeEnum getLastHurtingEnemy();
sourceTypeEnum getLastHurtingSource();
virtual void setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2, bool waitUnclear = false) override;
void setItemToBuy(ItemEntity* item);
ItemEntity* getItemToBuy();
void castTeleport();
virtual bool collideWithMap(int direction) override;
bool canAquireConsumable(enumItemType type);
int getConsumable(int n);
void setConsumable(int n, int type);
void dropConsumables(int n);
void tryToConsume(int n);
void consume(enumItemType item);
int getDamage();
+ int getDps();
+
+ bool seeInvisible();
protected:
virtual void readCollidingEntity(CollidingSpriteEntity* entity);
void generateBolt(float velx, float vely);
void rageFire(float damage, bool full, float velMult);
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void stuck() override;
private:
- int fireDamages;
+ int fireDamage;
+ int dps;
float fireVelocity;
float fireDelay;
float fireAnimationDelay;
float fireAnimationDelayMax;
int fireAnimationDirection;
bool canAnimateFire;
float spellAnimationDelay;
float spellAnimationDelayMax;
float currentFireDelay;
float randomFireDelay;
float rageFireDelay;
float hiccupDelay;
float boltLifeTime;
int gold;
int donation;
int criticalChance;
float invincibleDelay;
float divineInterventionDelay;
bool showCone;
bool isRegeneration;
bool isFairyTransmuted;
int lostHp[LAST_LEVEL];
bool canFirePlayer;
playerStatusEnum playerStatus;
float statusTimer;
enumItemType acquiredItem;
bool equip[NUMBER_EQUIP_ITEMS];
SpriteEntity* spriteItem;
SpriteEntity* spriteItemStar;
float specialBoltTimer;
enumShotType specialShots[SPECIAL_SHOT_SLOTS];
unsigned int specialShotLevel[SPECIAL_SHOT_SLOTS];
int specialShotIndex;
bool needInitShotType;
int collidingDirection; /*!< Colliding direction (4, 8, 6, 2) to detect collision with closed doors */
int firingDirection;
int keyDirection;
bool willCollideWithMap(int dx, int dy, bool checkMiddle);
std::vector<FairyEntity*> fairies;
float idleAge;
float endAge;
enemyTypeEnum lastHurtingEnemy;
sourceTypeEnum lastHurtingSource;
int spriteDy;
void renderPlayer(sf::RenderTarget* app);
void renderHalo(sf::RenderTarget* app);
divinityStruct divinity;
bool shouldBeSavedFromDivinity;
void fallRock();
void initFallingGrid();
bool fallingGrid[MAP_WIDTH][MAP_HEIGHT];
/*!
* \brief init the current shot type.
*
* Init the current shot type.
* Called when the player get a new shot, or after a switch.
*/
void initShotType();
castSpellStruct activeSpell;
struct protectionStruct
{
bool active;
float value;
float timer;
} protection;
void castSummonsSlimeExplode();
void castFireball();
void castFreeze();
void castEarthquake();
void castProtection();
void castWeb();
void castSummonsFlower();
void castTransmuteFairy();
void castTimeStop();
void castLightning();
ItemEntity* itemToBuy;
float getBolPositionY();
void incrementDivInterventions();
int consumable[MAX_SLOT_CONSUMABLES];
void acquireConsumable(enumItemType type);
void reveal();
+
+ float lastTeleportSave;
};
#endif // PLAYERSPRITE_H
diff --git a/src/PumpkinEntity.cpp b/src/PumpkinEntity.cpp
index 751535d..870684d 100644
--- a/src/PumpkinEntity.cpp
+++ b/src/PumpkinEntity.cpp
@@ -1,185 +1,185 @@
#include "PumpkinEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
PumpkinEntity::PumpkinEntity(float x, float y, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_PUMPKIN), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = 40;
meleeDamages = 5;
this->invocated = invocated;
if (invocated)
{
type = ENTITY_ENEMY_INVOCATED;
jumpingDelay = 0.1f;
age = 0.0f;
}
else
{
jumpingDelay = 0.6f + 0.1f * (rand() % 20);
}
enemyType = invocated ? EnemyTypePumpkin_invocated : EnemyTypePumpkin;
bloodColor = BloodGreen;
frame = 2;
shadowFrame = 3;
dyingFrame = 4;
deathFrame = FRAME_CORPSE_PUMPKIN;
agonizingSound = SOUND_PUMPKIN_DIE;
isJumping = false;
h = 0.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
}
void PumpkinEntity::animate(float delay)
{
float slimeDelay = delay;
if (specialState[SpecialStateIce].active) slimeDelay = delay * specialState[SpecialStateIce].param1;
if (!isAgonising)
{
if (isJumping)
{
hVelocity -= 700.0f * slimeDelay;
h += hVelocity * slimeDelay;
bool firstTimeGround = false;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
firstTimeGround = true;
hVelocity = 120.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
}
else
{
jumpingDelay = 0.05f;
isJumping = false;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
}
}
}
if (firstTimeGround) frame = 2;
else if (hVelocity > -60.0f) frame = 1;
else frame = 0;
}
else
{
jumpingDelay -= slimeDelay;
if (jumpingDelay < 0.0f)
{
if (rand() % 2 == 0)
SoundManager::getInstance().playSound(SOUND_PUMPKIN_01, false);
else
SoundManager::getInstance().playSound(SOUND_PUMPKIN_00, false);
hVelocity = 200.0f;
isJumping = true;
isFirstJumping = true;
float randVel = 280.0f;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), randVel ));
}
else if (jumpingDelay < 0.05f)
frame = 1;
else frame = 2;
}
if (age > 0.0f && jumpingDelay < 0.05f)
{
frame = (int)(age * 6) % 4;
if (frame == 3) frame = 1;
}
else frame = 2;
}
EnemyEntity::animate(delay);
z = y + 14;
}
void PumpkinEntity::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, 0, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void PumpkinEntity::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 14;
boundingBox.height = 28;
}
void PumpkinEntity::collideMapRight()
{
velocity.x = -velocity.x * 0.8f;
}
void PumpkinEntity::collideMapLeft()
{
velocity.x = -velocity.x * 0.8f;
}
void PumpkinEntity::collideMapTop()
{
velocity.y = -velocity.y * 0.8f;
}
void PumpkinEntity::collideMapBottom()
{
velocity.y = -velocity.y * 0.8f;
}
void PumpkinEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking)
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), velocity.norm() ));
}
void PumpkinEntity::dying()
{
EnemyEntity::dying();
hVelocity = 320.0f;
}
void PumpkinEntity::prepareDying()
{
dying();
}
diff --git a/src/RatEntity.cpp b/src/RatEntity.cpp
index ee97c1b..8b18ef5 100644
--- a/src/RatEntity.cpp
+++ b/src/RatEntity.cpp
@@ -1,224 +1,224 @@
#include "RatEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
RatEntity::RatEntity(float x, float y, ratTypeEnum ratType, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_RAT), x, y)
{
this->ratType = ratType;
imagesProLine = 10;
this->invocated = invocated;
doesAccelerate = true;
if (ratType == RatTypeNormal)
{
frame = 1;
dyingFrame = 9;
deathFrame = FRAME_CORPSE_RAT;
if (invocated) enemyType = EnemyTypeRat_invocated;
else enemyType = EnemyTypeRat;
hp = RAT_HP;
creatureSpeed = RAT_SPEED;
}
else //(ratType == RatTypeHelmet)
{
frame = 31;
dyingFrame = 39;
deathFrame = FRAME_CORPSE_RAT_HELMET;
if (invocated) enemyType = EnemyTypeRatHelmet_invocated;
else enemyType = EnemyTypeRatHelmet;
hp = RAT_HP_HELMET;
creatureSpeed = RAT_SPEED_HELMET;
}
direction = rand() % 4;
clockTurn = rand() % 2 == 0;
compute(false);
timer = 6 + rand() % 6;
meleeDamages = RAT_DAMAGES;
bloodColor = BloodRed;
shadowFrame = -1;
agonizingSound = SOUND_RAT_DYING;
sprite.setOrigin(32.0f, 38.0f);
}
void RatEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
timer -= delay;
if (timer < 0.0f)
{
timer = 6 + rand() % 6;
if (rand() % 3 == 0) clockTurn = !clockTurn;
compute(true);
}
checkNextFacing(delay);
frame = ((int)(age * 7.0f)) % 4;
if (frame == 3) frame = 1;
if (facingDirection == 4 || facingDirection == 6) frame += 3;
isMirroring = (facingDirection == 6 );
if (facingDirection == 8) frame += 6;
if (ratType == RatTypeHelmet) frame += 30;
}
EnemyEntity::animate(delay);
z = y + 17;
}
void RatEntity::compute(bool turn)
{
if (turn)
{
if (clockTurn)
{
direction++;
if (direction == 4) direction = 0;
}
else
{
direction--;
if (direction < 0) direction = 3;
}
}
velocity = Vector2D{0, 0};
float accelerationAbs = (enemyType == EnemyTypeRatHelmet || enemyType == EnemyTypeRatHelmet_invocated) ? (creatureSpeed / 10) : (creatureSpeed / 20);
doesAccelerate = true;
switch (direction)
{
case 0:
acceleration.x = 0;
acceleration.y = -accelerationAbs;
nextFacingDirection = 8;
break;
case 1:
acceleration.x = accelerationAbs;
acceleration.y = 0;
nextFacingDirection = 6;
break;
case 2:
acceleration.x = 0;
acceleration.y = accelerationAbs;
nextFacingDirection = 2;
break;
case 3:
acceleration.x = -accelerationAbs;
acceleration.y = 0;
nextFacingDirection = 4;
break;
}
}
void RatEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + RAT_BB_LEFT;
boundingBox.width = width - RAT_BB_WIDTH_DIFF;
boundingBox.top = (int)y - 13;
boundingBox.height = 31;
}
void RatEntity::collideMapRight()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
compute(true);
}
void RatEntity::collideMapLeft()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
compute(true);
}
void RatEntity::collideMapTop()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
compute(true);
}
void RatEntity::collideMapBottom()
{
- if (recoil.active) recoil.active = false;
+ if (repulsion.active) repulsion.active = false;
compute(true);
}
void RatEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
Vector2D recoilVector = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 50.0f);
- giveRecoil(false, recoilVector, 0.2f);
+ giveRepulsion(false, recoilVector, 0.2f);
compute(true);
}
}
void RatEntity::collideWithBolt(BoltEntity* boltEntity)
{
if (ratType == RatTypeHelmet && boltEntity->getBoltType() != ShotTypeIllusion)
{
int collisionDir = getCollisionDirection(boltEntity);
bool boltCollide = true;
switch (facingDirection)
{
case 4:
if (collisionDir == 7 || collisionDir == 4 || collisionDir == 1) boltCollide = false;
break;
case 2:
if (collisionDir == 1 || collisionDir == 2 || collisionDir == 3) boltCollide = false;
break;
case 6:
if (collisionDir == 9 || collisionDir == 6 || collisionDir == 3) boltCollide = false;
break;
case 8:
if (collisionDir == 7 || collisionDir == 8 || collisionDir == 9) boltCollide = false;
break;
}
if (boltCollide) EnemyEntity::collideWithBolt(boltEntity);
else
{
float xs = (x + boltEntity->getX()) / 2;
float ys = (y + boltEntity->getY()) / 2;
boltEntity->collide();
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
SoundManager::getInstance().playSound(SOUND_CLANG_00);
boltEntity->loseDamages(boltEntity->getDamages());
if (boltEntity->getBoltType() == ShotTypeStone)
{
float factor = (boltEntity->isFromPlayer() && game().getPlayer()->isEquiped(EQUIP_RAPID_SHOT)) ? 0.25f : 1.0f;
float recoilVelocity = factor * STONE_DECOIL_VELOCITY[boltEntity->getLevel()];
float recoilDelay = factor * STONE_DECOIL_DELAY[boltEntity->getLevel()];
Vector2D recoilVector = Vector2D(0, 0).vectorTo(boltEntity->getVelocity(),
recoilVelocity );
- giveRecoil(true, recoilVector, recoilDelay);
+ giveRepulsion(true, recoilVector, recoilDelay);
}
}
}
else EnemyEntity::collideWithBolt(boltEntity);
}
void RatEntity::drop()
{
if (!invocated) EnemyEntity::drop();
}
diff --git a/src/RockMissileEntity.cpp b/src/RockMissileEntity.cpp
index e613794..74985fb 100644
--- a/src/RockMissileEntity.cpp
+++ b/src/RockMissileEntity.cpp
@@ -1,139 +1,139 @@
#include "RockMissileEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
RockMissileEntity::RockMissileEntity(float x, float y, int rockType)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_CYCLOP), x, y)
{
Vector2D targetPos = game().getPlayerPosition();
imagesProLine = 20;
collisionDirection = -1;
enemyType = EnemyTypeRockMissile;
movingStyle = movFlying;
bloodColor = BloodNone; // stones don't bleed
hasCollided = false;
age = 0.0f;
this->rockType = rockType;
if (rockType == 0)
{
creatureSpeed = 500.0f;
hp = 12;
meleeDamages = 5;
frame = 18;
}
else
{
creatureSpeed = 450.0f;
hp = 24;
meleeDamages = 8;
frame = 38;
}
setVelocity(Vector2D(x, y).vectorNearlyTo(targetPos, creatureSpeed, 0.4f));
canExplode = false;
if (y < TILE_HEIGHT) this->y = TILE_HEIGHT;
if (!testEntityInMap()) isDying = true;
resistance[ResistancePoison] = ResistanceImmune;
}
void RockMissileEntity::animate(float delay)
{
EnemyEntity::animate(delay);
if (x < -60 || x > 1050 || y < - 50 || y > 800) isDying = true;
}
void RockMissileEntity::calculateBB()
{
int w;
if (rockType == 0) w = 20;
else w = 24;
boundingBox.left = (int)x - w / 2;
boundingBox.width = w;
boundingBox.top = (int)y - w / 2;
boundingBox.height = w;
}
void RockMissileEntity::collideWall()
{
if (rockType == 1 && !hasCollided)
{
hasCollided = true;
if (collisionDirection == DIRECTION_RIGHT || collisionDirection == DIRECTION_LEFT) velocity.x = -velocity.x;
else velocity.y = -velocity.y;
}
else
{
dying();
}
}
void RockMissileEntity::collideMapRight()
{
collisionDirection = DIRECTION_RIGHT;
collideWall();
}
void RockMissileEntity::collideMapLeft()
{
collisionDirection = DIRECTION_LEFT;
collideWall();
}
void RockMissileEntity::collideMapTop()
{
collisionDirection = DIRECTION_TOP;
collideWall();
}
void RockMissileEntity::collideMapBottom()
{
collisionDirection = DIRECTION_BOTTOM;
collideWall();
}
void RockMissileEntity::collideWithEnemy(EnemyEntity* entity)
{
}
void RockMissileEntity::dying()
{
isDying = true;
game().addKilledEnemy(enemyType, hurtingType);
SoundManager::getInstance().playSound(
rockType == 0 ? SOUND_ROCK_IMPACT_LIGHT : SOUND_ROCK_IMPACT_MEDIUM);
game().makeShake(0.1f);
for (int i = 0; i < 4; i++)
{
displayEntityStruct& de = game().getCurrentMapEntity()->generateBlood(x, y, BloodRock);
if ((collisionDirection == DIRECTION_LEFT) && (de.velocity.x < 0.0f))
de.velocity.x *= -0.25f;
else if ((collisionDirection == DIRECTION_RIGHT) && (de.velocity.x > 0.0f))
de.velocity.x *= -0.25f;
else if ((collisionDirection == DIRECTION_TOP) && (de.velocity.y < 0.0f))
de.velocity.y *= -0.25f;
else if ((collisionDirection == DIRECTION_BOTTOM) && (de.velocity.y > 0.0f))
de.velocity.y *= -0.25f;
}
}
-void RockMissileEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
+void RockMissileEntity::inflictsRepulsionTo(BaseCreatureEntity* targetEntity)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
- Vector2D recoilVector = Vector2D(0, 0).vectorTo(getVelocity(), 600.0f );
- targetEntity->giveRecoil(true, recoilVector, 0.5f);
+ Vector2D repulsionVector = Vector2D(0, 0).vectorTo(getVelocity(), 600.0f );
+ targetEntity->giveRepulsion(true, repulsionVector, 0.5f);
}
}
diff --git a/src/RockMissileEntity.h b/src/RockMissileEntity.h
index 993912f..2d2aa76 100644
--- a/src/RockMissileEntity.h
+++ b/src/RockMissileEntity.h
@@ -1,30 +1,30 @@
#ifndef ROCKMISSILENTITY_H
#define ROCKMISSILENTITY_H
#include "EnemyEntity.h"
class RockMissileEntity : public EnemyEntity
{
public:
RockMissileEntity(float x, float y, int rockType);
virtual void animate(float delay);
virtual void calculateBB();
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void collideWithEnemy(EnemyEntity* entity) override;
- virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
+ virtual void inflictsRepulsionTo(BaseCreatureEntity* targetEntity) override;
virtual void dying();
private:
int collisionDirection;
int rockType;
bool hasCollided;
void collideWall();
};
#endif // ROCKMISSILENTITY_H
diff --git a/src/SausageEntity.cpp b/src/SausageEntity.cpp
index b3777a6..3b3fb5f 100644
--- a/src/SausageEntity.cpp
+++ b/src/SausageEntity.cpp
@@ -1,190 +1,190 @@
#include "SausageEntity.h"
#include "ExplosionEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SausageEntity::SausageEntity(float x, float y, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_SAUSAGE), x, y)
{
creatureSpeed = 0.0f;
hp = 8;
meleeDamages = 5;
this->invocated = invocated;
if (invocated) type = ENTITY_ENEMY_INVOCATED;
age = 0.0f;
enemyType = invocated ? EnemyTypeSausage_invocated : EnemyTypeSausage;
frame = 1;
shadowFrame = 3;
deathFrame = FRAME_CORPSE_SLIME_VIOLET;
agonizingSound = SOUND_NONE;
width = 48;
height = 48;
sprite.setOrigin(24, 24);
h = 0;
state = 0;
timer = 0.5f + 0.1f *( rand() % 12);
canExplode = false;
isNew = true;
}
void SausageEntity::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, 0, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void SausageEntity::animate(float delay)
{
isNew = false;
float sausDelay = delay;
if (specialState[SpecialStateIce].active) sausDelay = delay * specialState[SpecialStateIce].param1;
timer -= sausDelay;
if (timer <= 0.0f)
{
if (state == 0) // waiting
{
state++;
hVelocity = 450.0f;
}
else if (state == 2) // waiting in air
{
state++;
}
}
if (state == 1 || state == 3) // jumping / falling
{
hVelocity -= 750.0f * sausDelay;
if (state == 1 && /*hVelocity <= 0.0f*/ h > 35)
{
state = 2;
timer = 0.4f;
hVelocity = -100.0f;
}
else
h += hVelocity * sausDelay;
if (h <= 0.0f)
{
h = 0.0f;
state = 0;
timer = 1.0f;
}
}
if (h > 1.0f)
{
frame = ((int)(age * 20)) % 4;
if (frame == 3) frame = 1;
}
else
frame = 1;
EnemyEntity::animate(delay);
// frame
z = y + 14;
}
bool SausageEntity::canCollide()
{
return (!isNew);
}
void SausageEntity::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 14;
boundingBox.height = 28;
}
void SausageEntity::collideMapRight()
{
velocity.x = -velocity.x * 0.8f;
}
void SausageEntity::collideMapLeft()
{
velocity.x = -velocity.x * 0.8f;
}
void SausageEntity::collideMapTop()
{
velocity.y = -velocity.y * 0.8f;
}
void SausageEntity::collideMapBottom()
{
velocity.y = -velocity.y * 0.8f;
}
void SausageEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead()) dying();
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f) collideWithBolt(boltEntity);
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
void SausageEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 50.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
}
void SausageEntity::dying()
{
EnemyEntity::dying();
new ExplosionEntity(x, y, ExplosionTypeStandard, 18, EnemyTypeNone, true);
SoundManager::getInstance().playSound(SOUND_BOOM_00);
}
void SausageEntity::drop()
{
if (!invocated) EnemyEntity::drop();
}
diff --git a/src/SlimeEntity.cpp b/src/SlimeEntity.cpp
index 8a1b626..bfaea90 100644
--- a/src/SlimeEntity.cpp
+++ b/src/SlimeEntity.cpp
@@ -1,403 +1,416 @@
#include "SlimeEntity.h"
#include "PlayerEntity.h"
#include "EnemyBoltEntity.h"
#include "ExplosionEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SlimeEntity::SlimeEntity(float x, float y, slimeTypeEnum slimeType, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_SLIME), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = SLIME_HP;
meleeDamages = SLIME_DAMAGES;
this->slimeType = slimeType;
this->invocated = invocated;
if (invocated)
{
type = ENTITY_ENEMY_INVOCATED;
jumpingDelay = 0.1f;
age = 0.0f;
}
else
{
jumpingDelay = 0.6f + 0.1f * (rand() % 20);
}
if (slimeType == SlimeTypeBlue)
{
resistance[ResistanceFrozen] = ResistanceImmune;
resistance[ResistanceIce] = ResistanceVeryHigh;
resistance[ResistanceFire] = ResistanceVeryLow;
enemyType = invocated ? EnemyTypeSlimeBlue_invocated : EnemyTypeSlimeBlue;
deathFrame = FRAME_CORPSE_SLIME_BLUE;
}
else if (slimeType == SlimeTypeRed)
{
resistance[ResistanceIce] = ResistanceVeryLow;
resistance[ResistanceFire] = ResistanceVeryHigh;
enemyType = invocated ? EnemyTypeSlimeRed_invocated : EnemyTypeSlimeRed;
deathFrame = FRAME_CORPSE_SLIME_RED;
}
else if (slimeType == SlimeTypeViolet)
{
enemyType = invocated ? EnemyTypeSlimeViolet_invocated : EnemyTypeSlimeViolet;
canExplode = false;
deathFrame = FRAME_CORPSE_SLIME_VIOLET;
}
else
{
enemyType = invocated ? EnemyTypeSlime_invocated : EnemyTypeSlime;
deathFrame = FRAME_CORPSE_SLIME;
}
bloodColor = BloodGreen;
frame = 0;
shadowFrame = 3;
imagesProLine = 4;
isJumping = false;
h = 0.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
isPet = false;
willExplode = false;
+ noCollisionTimer = -1.0f;
+}
+
+void SlimeEntity::setH(float h)
+{
+ this->h = h;
}
void SlimeEntity::animate(float delay)
{
+ if (noCollisionTimer > 0.0f) noCollisionTimer -= delay;
float slimeDelay = delay;
if (specialState[SpecialStateIce].active) slimeDelay = delay * specialState[SpecialStateIce].param1;
if (isJumping)
{
hVelocity -= 700.0f * slimeDelay;
h += hVelocity * slimeDelay;
bool firstTimeGround = false;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
firstTimeGround = true;
hVelocity = 160.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
if (slimeType == SlimeTypeBlue || slimeType == SlimeTypeRed)
fire();
else if (slimeType == SlimeTypeViolet)
{
hp = 0;
dying();
}
}
else
{
jumpingDelay = 0.4f + 0.1f * (rand() % 20);
isJumping = false;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
}
}
}
if (firstTimeGround) frame = 0;
else if (hVelocity > -190.0f) frame = 2;
else frame = 1;
}
else
{
jumpingDelay -= slimeDelay;
if (jumpingDelay < 0.0f)
{
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
hVelocity = 350.0f + rand() % 300;
isJumping = true;
isFirstJumping = true;
float randVel = 250.0f + rand() % 250;
if (!game().getPlayer()->isEquiped(EQUIP_MANUAL_SLIMES) && rand() % 2 == 0)
{
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), randVel ));
}
else
velocity = Vector2D(randVel);
}
else if (jumpingDelay < 0.1f)
frame = 1;
else frame = 0;
}
EnemyEntity::animate(delay);
z = y + 14;
}
void SlimeEntity::makeExplode()
{
if (isJumping)
willExplode = true;
else
BaseCreatureEntity::makeExplode();
}
void SlimeEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (!isPet && (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT ) )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead())
{
if (playerEntity->hurt(getHurtParams(meleeDamages, meleeType, meleeDamages, false, SourceTypeMelee, enemyType, false)))
{
float xs = (x + playerEntity->getX()) / 2;
float ys = (y + playerEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
- inflictsRecoilTo(playerEntity);
+ inflictsRepulsionTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
collideWithBolt(boltEntity);
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
void SlimeEntity::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, slimeType * height, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void SlimeEntity::calculateBB()
{
if (isPet)
{
boundingBox.left = (int)x - 1;
boundingBox.width = 2;
boundingBox.top = (int)y - 1;
boundingBox.height = 2;
}
else
{
boundingBox.left = (int)x - width / 2 + SLIME_BB_LEFT;
boundingBox.width = width - SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + SLIME_BB_TOP - 15;
boundingBox.height = height - SLIME_BB_HEIGHT_DIFF;
}
}
void SlimeEntity::collideMapRight()
{
// if (x > OFFSET_X + MAP_WIDTH * TILE_WIDTH)
velocity.x = -velocity.x * 0.8f;
}
void SlimeEntity::collideMapLeft()
{
// if (x < OFFSET_X + MAP_WIDTH )
velocity.x = -velocity.x * 0.8f;
}
void SlimeEntity::collideMapTop()
{
// if (y > OFFSET_Y + MAP_HEIGHT * TILE_HEIGHT)
velocity.y = -velocity.y * 0.8f;
}
void SlimeEntity::collideMapBottom()
{
// if (y < OFFSET_Y + MAP_HEIGHT )
velocity.y = -velocity.y * 0.8f;
}
void SlimeEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking)
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 100.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
computeFacingDirection();
}
}
bool SlimeEntity::collideWithMap(int direction)
{
calculateBB();
int xTile0 = (boundingBox.left - offsetX) / tileWidth;
int xTilef = (boundingBox.left + boundingBox.width - offsetX) / tileWidth;
int yTile0 = (boundingBox.top - offsetY) / tileHeight;
int yTilef = (boundingBox.top + boundingBox.height - offsetY) / tileHeight;
if (boundingBox.top < 0) yTile0 = -1;
for (int xTile = xTile0; xTile <= xTilef; xTile++)
for (int yTile = yTile0; yTile <= yTilef; yTile++)
{
if (!game().getCurrentMap()->isFlyable(xTile, yTile))
{
switch (direction)
{
case DIRECTION_LEFT:
if (map->isLeftBlocking(xTile, yTile)) return true;
break;
case DIRECTION_RIGHT:
if (map->isRightBlocking(xTile, yTile)) return true;
break;
case DIRECTION_TOP:
if (map->isUpBlocking(xTile, yTile)) return true;
break;
case DIRECTION_BOTTOM:
if (map->isDownBlocking(xTile, yTile)) return true;
break;
}
}
}
return false;
}
void SlimeEntity::dying()
{
if (slimeType == SlimeTypeViolet) explode();
else if (willExplode) BaseCreatureEntity::makeExplode();
EnemyEntity::dying();
}
void SlimeEntity::prepareDying()
{
if (!isJumping)
dying();
}
bool SlimeEntity::canCollide()
{
+ if (noCollisionTimer > 0.0f) return false;
return h <= 70.0f && hp > 0;
}
+void SlimeEntity::disableCollidingTemporary()
+{
+ noCollisionTimer = 0.8f;
+}
+
BaseCreatureEntity::enumMovingStyle SlimeEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
void SlimeEntity::fire()
{
for (int i = 0; i < 4; i++)
{
EnemyBoltEntity* bolt;
if (slimeType == SlimeTypeBlue)
{
bolt = new EnemyBoltEntity(x, y, ShotTypeIce, 0, enemyType);
bolt->setDamages(5);
}
else if (slimeType == SlimeTypeRed)
{
bolt = new EnemyBoltEntity(x, y, ShotTypeFire, 0, enemyType);
bolt->setDamages(8);
}
else
return;
switch (i)
{
case 0: bolt->setVelocity(Vector2D(SLIME_FIRE_VELOCITY, 0)); break;
case 1: bolt->setVelocity(Vector2D(-SLIME_FIRE_VELOCITY, 0)); break;
case 2: bolt->setVelocity(Vector2D(0, SLIME_FIRE_VELOCITY)); break;
case 3: bolt->setVelocity(Vector2D(0, -SLIME_FIRE_VELOCITY)); break;
}
}
if (slimeType == SlimeTypeBlue) SoundManager::getInstance().playSound(SOUND_BLAST_ICE);
else if (slimeType == SlimeTypeRed) SoundManager::getInstance().playSound(SOUND_BLAST_FIRE);
else SoundManager::getInstance().playSound(SOUND_BLAST_FLOWER);
}
void SlimeEntity::explode()
{
int damage = 12;
if (isPet) damage = game().getPlayer()->isEquiped(EQUIP_BOOK_MAGIC_II) ? 24 : 18;
new ExplosionEntity(x, y, ExplosionTypeStandard, damage, enemyType, true);
game().makeShake(1.0f);
SoundManager::getInstance().playSound(SOUND_BOOM_00);
}
void SlimeEntity::makePet(int direction)
{
isPet = true;
hVelocity = 450.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
isJumping = true;
isFirstJumping = true;
switch (direction)
{
case 4: velocity.x = -350.0f; velocity.y = -0; break;
case 6: velocity.x = 350.0f; velocity.y = -0; break;
case 2: velocity.y = 350.0f; velocity.x = -0; break;
default: velocity.y = -350.0f; velocity.x = -0; break;
}
}
void SlimeEntity::drop()
{
if (!invocated) EnemyEntity::drop();
}
diff --git a/src/SlimeEntity.h b/src/SlimeEntity.h
index 70d8782..b10dbf4 100644
--- a/src/SlimeEntity.h
+++ b/src/SlimeEntity.h
@@ -1,49 +1,52 @@
#ifndef SLIMESPRITE_H
#define SLIMESPRITE_H
#include "EnemyEntity.h"
#include "PlayerEntity.h"
enum slimeTypeEnum { SlimeTypeStandard, SlimeTypeRed, SlimeTypeBlue, SlimeTypeViolet };
class SlimeEntity : public EnemyEntity
{
public:
SlimeEntity(float x, float y, slimeTypeEnum slimeType, bool invocated);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
virtual bool canCollide();
void makePet(int direction);
+ void setH(float h);
+ void disableCollidingTemporary();
protected:
virtual bool collideWithMap(int direction);
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void readCollidingEntity(CollidingSpriteEntity* entity);
virtual void collideWithEnemy(EnemyEntity* entity) override;
virtual void dying();
virtual void prepareDying();
virtual void drop();
virtual enumMovingStyle getMovingStyle();
virtual void makeExplode();
private:
float jumpingDelay;
+ float noCollisionTimer;
bool isJumping;
bool isPet;
bool isFirstJumping;
bool invocated;
slimeTypeEnum slimeType;
void fire();
void explode();
bool willExplode;
};
#endif // SLIMESPRITE_H
diff --git a/src/SlimePetEntity.cpp b/src/SlimePetEntity.cpp
index 0620166..c3d9bb2 100644
--- a/src/SlimePetEntity.cpp
+++ b/src/SlimePetEntity.cpp
@@ -1,307 +1,307 @@
#include "SlimePetEntity.h"
#include "PlayerEntity.h"
#include "EnemyBoltEntity.h"
#include "ExplosionEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SlimePetEntity::SlimePetEntity()
: BaseCreatureEntity (ImageManager::getInstance().getImage(IMAGE_SLIME), 200, 200, 64, 64)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = SLIME_HP;
jumpingDelay = 0.6f + 0.1f * (rand() % 20);
type = ENTITY_FAMILIAR_LOCAL;
frame = 0;
shadowFrame = 3;
imagesProLine = 4;
isJumping = false;
h = 0.0f;
attackDelay = -1.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
h = 1300;
hVelocity = 0.0f;
isJumping = true;
isFirstJumping = true;
age = 0.0f;
x = GAME_WIDTH * 0.5f;
y = GAME_HEIGHT * 0.5f;
if (game().getPlayer()->getX() < 2 * TILE_WIDTH) x = 2.5f * TILE_WIDTH;
else if (game().getPlayer()->getX() > GAME_WIDTH - 2 * TILE_WIDTH) x = GAME_WIDTH - 2.5f * TILE_WIDTH;
else if (game().getPlayer()->getY() < 2 * TILE_HEIGHT) y = 2.5f * TILE_HEIGHT;
else if (game().getPlayer()->getY() > GAME_HEIGHT - 2 * TILE_HEIGHT) y = GAME_HEIGHT - 2.5f * TILE_HEIGHT;
}
void SlimePetEntity::animate(float delay)
{
if (age < 0.0f)
{
age += delay;
}
else
{
attackDelay -= delay;
if (isJumping)
{
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
bool firstTimeGround = false;
if (h <= 0.0f)
{
h = 0.0f;
if (isFalling())
{
fall();
}
else
{
if (isFirstJumping)
{
isFirstJumping = false;
firstTimeGround = true;
hVelocity = 160.0f;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT);
}
else
{
jumpingDelay = 0.3f + 0.1f * (rand() % 15);
isJumping = false;
SoundManager::getInstance().playSound(SOUND_SLIME_IMAPCT_WEAK);
}
}
}
if (firstTimeGround) frame = 0;
else if (hVelocity > -190.0f) frame = 2;
else frame = 1;
}
else if (isFalling())
{
fall();
}
else
{
jumpingDelay -= delay;
if (jumpingDelay < 0.0f)
{
SoundManager::getInstance().playSound(SOUND_SLIME_JUMP);
hVelocity = 300.0f + rand() % 250;
isJumping = true;
isFirstJumping = true;
float randVel = 250.0f + rand() % 250;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), randVel ));
}
else if (jumpingDelay < 0.1f)
frame = 1;
else frame = 0;
}
BaseCreatureEntity::animate(delay);
if (canCollide()) testSpriteCollisions();
}
z = y + 14;
}
void SlimePetEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (canCollide() && collideWithEntity(entity))
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide())
{
if (attackDelay <= 0.0f)
{
enemyEntity->hurt(getHurtParams(12, ShotTypeStandard,0, false, SourceTypeMelee, EnemyTypeNone,false));
attackDelay = 0.65f;
float xs = (x + enemyEntity->getX()) / 2;
float ys = (y + enemyEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
if (enemyEntity->getMovingStyle() == movWalking)
{
Vector2D vel = Vector2D(enemyEntity->getX(), enemyEntity->getY()).vectorTo(Vector2D(x, y), 100.0f );
- giveRecoil(false, vel, 0.3f);
+ giveRepulsion(false, vel, 0.3f);
}
}
}
}
}
void SlimePetEntity::render(sf::RenderTarget* app)
{
// shadow
if (h < 1055)
{
int fade = 255;
if (h > 800)
fade = - (h - 1055);
sprite.setColor(sf::Color(255, 255, 255, fade));
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
sprite.setColor(sf::Color(255, 255, 255, 255));
}
// sprite
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, 4 * height, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void SlimePetEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + SLIME_BB_LEFT;
boundingBox.width = width - SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + SLIME_BB_TOP - 15;
boundingBox.height = height - SLIME_BB_HEIGHT_DIFF;
}
void SlimePetEntity::collideMapRight()
{
velocity.x = -velocity.x * 0.8f;
}
void SlimePetEntity::collideMapLeft()
{
velocity.x = -velocity.x * 0.8f;
}
void SlimePetEntity::collideMapTop()
{
velocity.y = -velocity.y * 0.8f;
}
void SlimePetEntity::collideMapBottom()
{
velocity.y = -velocity.y * 0.8f;
}
void SlimePetEntity::changeRoom()
{
h = 1500;
hVelocity = 0.0f;
isJumping = true;
isFirstJumping = true;
x = 300;
y = 250;
}
bool SlimePetEntity::collideWithMap(int direction)
{
calculateBB();
int xTile0 = (boundingBox.left - offsetX) / tileWidth;
int xTilef = (boundingBox.left + boundingBox.width - offsetX) / tileWidth;
int yTile0 = (boundingBox.top - offsetY) / tileHeight;
int yTilef = (boundingBox.top + boundingBox.height - offsetY) / tileHeight;
if (boundingBox.top < 0) yTile0 = -1;
for (int xTile = xTile0; xTile <= xTilef; xTile++)
for (int yTile = yTile0; yTile <= yTilef; yTile++)
{
if (!game().getCurrentMap()->isFlyable(xTile, yTile))
{
switch (direction)
{
case DIRECTION_LEFT:
if (map->isLeftBlocking(xTile, yTile)) return true;
break;
case DIRECTION_RIGHT:
if (map->isRightBlocking(xTile, yTile)) return true;
break;
case DIRECTION_TOP:
if (map->isUpBlocking(xTile, yTile)) return true;
break;
case DIRECTION_BOTTOM:
if (map->isDownBlocking(xTile, yTile)) return true;
break;
}
}
}
return false;
}
bool SlimePetEntity::canCollide()
{
return h <= 70.0f;
}
bool SlimePetEntity::isFalling()
{
int tilex0 = boundingBox.left / TILE_WIDTH;
int tiley0 = boundingBox.top / TILE_HEIGHT;
int tilexf = (boundingBox.left + boundingBox.width) / TILE_WIDTH;
int tileyf = (boundingBox.top + boundingBox.height) / TILE_HEIGHT;
return (game().getCurrentMap()->getLogicalTile(tilex0, tiley0) == LogicalHole
&& game().getCurrentMap()->getLogicalTile(tilex0, tileyf) == LogicalHole
&& game().getCurrentMap()->getLogicalTile(tilexf, tileyf) == LogicalHole
&& game().getCurrentMap()->getLogicalTile(tilexf, tiley0) == LogicalHole);
}
void SlimePetEntity::fall()
{
SpriteEntity* spriteEntity
= new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_SLIME),
x,
y + 6,
64, 64, 4);
spriteEntity->setAge(0.0f);
spriteEntity->setLifetime(3.0f);
spriteEntity->setShrinking(true);
spriteEntity->setFading(true);
spriteEntity->setFrame(16);
isDying = true;
SlimePetEntity* newSlime = new SlimePetEntity();
newSlime->setAge(-4.0f);
}
BaseCreatureEntity::enumMovingStyle SlimePetEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
diff --git a/src/SnakeEntity.cpp b/src/SnakeEntity.cpp
index 54fde7e..6d0b5c6 100644
--- a/src/SnakeEntity.cpp
+++ b/src/SnakeEntity.cpp
@@ -1,213 +1,213 @@
#include "SnakeEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SnakeEntity::SnakeEntity(float x, float y, snakeTypeEnum snakeType, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_SNAKE), x, y)
{
this->snakeType = snakeType;
imagesProLine = 10;
this->invocated = invocated;
if (snakeType == SnakeTypeNormal)
{
frame = 0;
dyingFrame = 8;
deathFrame = FRAME_CORPSE_SNAKE;
if (invocated) enemyType = EnemyTypeSnake_invocated;
else enemyType = EnemyTypeSnake;
hp = SNAKE_HP;
creatureSpeed = SNAKE_SPEED;
meleeDamages = SNAKE_DAMAGE;
meleeType = ShotTypePoison;
}
else // snake blood
{
frame = 10;
dyingFrame = 18;
deathFrame = FRAME_CORPSE_SNAKE_BLOOD;
if (invocated) enemyType = EnemyTypeSnakeBlood_invocated;
else enemyType = EnemyTypeSnakeBlood;
hp = SNAKE_BLOOD_HP;
creatureSpeed = SNAKE_BLOOD_SPEED;
meleeDamages = SNAKE_BLOOD_DAMAGE;
}
velocity = Vector2D(creatureSpeed);
computeFacingDirection();
bloodColor = BloodRed;
shadowFrame = 9;
timer = -1.0f;
agonizingSound = SOUND_SNAKE_DIE;
resistance[ResistancePoison] = ResistanceImmune;
}
void SnakeEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
sprite.setColor(sf::Color(255,255,255,255));
timer = timer - delay;
if (timer <= 0.0f)
{
timer = 0.8f;
if (canWalkTo(game().getPlayerPosition().x, game().getPlayerPosition().y))
{
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
computeFacingDirection();
}
}
switch (facingDirection)
{
case 2: frame = 0; break;
case 4: frame = 2; break;
case 6: frame = 4; break;
case 8: frame = 6; break;
}
frame += ((int)(age * 3.0f)) % 2;
if (snakeType == SnakeTypeBlood) frame += 10;
}
EnemyEntity::animate(delay);
z = y + 15;
}
void SnakeEntity::calculateBB()
{
boundingBox.left = (int)x - 15;
boundingBox.width = 30;
boundingBox.top = (int)y - 15;
boundingBox.height = 30;
}
void SnakeEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
else computeFacingDirection();
}
void SnakeEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active) recoil.velocity.x = -recoil.velocity.x;
+ if (repulsion.active) repulsion.velocity.x = -repulsion.velocity.x;
else computeFacingDirection();
}
void SnakeEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
else computeFacingDirection();
}
void SnakeEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active) recoil.velocity.y = -recoil.velocity.y;
+ if (repulsion.active) repulsion.velocity.y = -repulsion.velocity.y;
else computeFacingDirection();
}
void SnakeEntity::collideWithEnemy(EnemyEntity* entity)
{
- if (recoil.active && recoil.stun) return;
+ if (repulsion.active && repulsion.stun) return;
if (entity->getMovingStyle() == movWalking )
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), creatureSpeed );
if (velocity.x * vel.x < 0) velocity.x = vel.x;
if (velocity.y * vel.y < 0) velocity.y = vel.y;
timer = 0.05f;
}
}
void SnakeEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead())
{
int meleeLevel = 0;
if (snakeType == SnakeTypeBlood)
{
if (rand() % 3 == 0)
{
meleeType = ShotTypePoison;
meleeDamages = 4;
meleeLevel = 1;
}
else
{
meleeType = ShotTypeStandard;
meleeDamages = 8;
}
}
if (playerEntity->hurt(getHurtParams(meleeDamages, meleeType, meleeLevel, false, SourceTypeMelee, enemyType, false)))
{
float xs = (x + playerEntity->getX()) / 2;
float ys = (y + playerEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
- inflictsRecoilTo(playerEntity);
+ inflictsRepulsionTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
collideWithBolt(boltEntity);
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
void SnakeEntity::drop()
{
if (!invocated)
{
if (rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
if (game().getPlayer()->isEquiped(EQUIP_LUCK) && rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
if (rand() % 25 == 0)
{
dropItem(ItemHealthVerySmallPoison);
}
}
}
diff --git a/src/SpiderEggEntity.cpp b/src/SpiderEggEntity.cpp
index 078269b..287351f 100644
--- a/src/SpiderEggEntity.cpp
+++ b/src/SpiderEggEntity.cpp
@@ -1,163 +1,163 @@
#include "SpiderEggEntity.h"
#include "PlayerEntity.h"
#include "LittleSpiderEntity.h"
#include "BoltEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SpiderEggEntity::SpiderEggEntity(float x, float y, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_SPIDER_EGG), x, y)
{
imagesProLine = 20;
type = invocated ? ENTITY_ENEMY_INVOCATED : ENTITY_ENEMY;
enemyType = invocated ? EnemyTypeSpiderEgg_invocated : EnemyTypeSpiderEgg;
this->invocated = invocated;
movingStyle = movFlying;
bloodColor = invocated ? BloodNone : BloodGreen;
deathFrame = FRAME_CORPSE_SPIDER_EGG;
dyingSound = rand() % 2 == 0 ? SOUND_EGG_SMASH_00 : SOUND_EGG_SMASH_01;
meleeDamages = 5;
age = 0.0f;
h = 1800 + rand() % 1000;
hp = 24;
jumping = false;
hVelocity = 0.0f;
lifetime = 12.0f + (rand() % 700) / 100;
}
void SpiderEggEntity::animate(float delay)
{
if (movingStyle == movFlying)
{
if (jumping)
{
hVelocity -= 300.0f * delay;
h += hVelocity * delay;
if (h <= 0.0f)
{
movingStyle = movWalking;
h = 0.0f;
}
}
else
{
h -= delay * 750.0f;
if (h < 0.0f)
{
h = 0.0f;
hVelocity = 100.0f;
jumping = true;
}
}
}
EnemyEntity::animate(delay);
z = y + 25;
frame = 0;
if (lifetime - age < 1.0f) frame = 3;
else if (lifetime - age < 2.5f) frame = 2;
else if (lifetime - age < 5.0f) frame = 1;
}
void SpiderEggEntity::dyingFromAge()
{
LittleSpiderEntity* ls = new LittleSpiderEntity(x, y, SpiderTypeStandard, invocated);
ls->setAge(-0.3f);
for (int i = 0; i < 4; i++)
{
SpriteEntity* blood = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_BLOOD), x, y, 16, 16, 6);
blood->setZ(-1);
blood->setFrame(18 + rand()%6);
blood->setType(ENTITY_BLOOD);
blood->setVelocity(Vector2D(rand()%250));
blood->setViscosity(0.95f);
float bloodScale = 1.0f + (rand() % 10) * 0.1f;
blood->setScale(bloodScale, bloodScale);
}
SoundManager::getInstance().playSound(SOUND_EGG_SMASH_00 + rand() % 2);
isDying = true;
}
void SpiderEggEntity::render(sf::RenderTarget* app)
{
int nx = frame % imagesProLine;
int ny = frame / imagesProLine;
// shadow
if (h <= 1600)
{
int f = 1600 - h;
if (f > 255) f = 255;
sprite.setColor(sf::Color(255, 255, 255, f));
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(4 * width, 0, width, height));
app->draw(sprite);
sprite.setColor(sf::Color(255, 255, 255, 255));
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void SpiderEggEntity::calculateBB()
{
boundingBox.left = (int)x - 20;
boundingBox.width = 40;
boundingBox.top = (int)y - 5;
boundingBox.height = 30;
}
bool SpiderEggEntity::canCollide()
{
return h < 70;
}
void SpiderEggEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (movingStyle == movFlying && playerEntity != NULL && !playerEntity->isDead())
{
if (playerEntity->hurt(getHurtParams(meleeDamages, ShotTypeStandard, 0, false, SourceTypeMelee, enemyType, false)))
{
float xs = (x + playerEntity->getX()) / 2;
float ys = (y + playerEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
- inflictsRecoilTo(playerEntity);
+ inflictsRepulsionTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
EnemyEntity::collideWithBolt(boltEntity);
}
}
}
}
diff --git a/src/SpiderWebEntity.cpp b/src/SpiderWebEntity.cpp
index fbb6597..134f330 100644
--- a/src/SpiderWebEntity.cpp
+++ b/src/SpiderWebEntity.cpp
@@ -1,154 +1,154 @@
#include "SpiderWebEntity.h"
#include "PlayerEntity.h"
#include "BoltEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SpiderWebEntity::SpiderWebEntity(float x, float y, bool isFromPlayer)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_SPIDER_WEB), x, y)
{
imagesProLine = 20;
type = ENTITY_ENEMY_NC;
enemyType = EnemyTypeSpiderWeb;
movingStyle = movFlying;
bloodColor = BloodNone; // web don't bleed
deathFrame = FRAME_CORPSE_SPIDER_WEB;
this->isFromPlayer = isFromPlayer;
age = 0.0f;
frame = 0;
setVelocity(Vector2D(80 + rand()% 500));
width = 128.0f;
height = 128.0f;
sprite.setOrigin(64.0f, 64.0f);
viscosity = 0.97f;
hp = 40;
hpMax = 40;
resistance[ResistanceFrozen] = ResistanceImmune;
- resistance[ResistanceRecoil] = ResistanceImmune;
+ resistance[ResistanceRepulsion] = ResistanceImmune;
resistance[ResistanceFire] = ResistanceVeryLow;
resistance[ResistanceStone] = ResistanceVeryLow;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
if (!testEntityInMap()) isDying = true;
}
void SpiderWebEntity::animate(float delay)
{
EnemyEntity::animate(delay);
if (age <= 0.5f)
{
float scale = age * 2.0f;
sprite.setScale(scale,scale);
}
else
sprite.setScale(1.0f, 1.0f);
int color = 177 + 78 * hp / hpMax;
sprite.setColor(sf::Color(color, color, color, 255));
}
void SpiderWebEntity::calculateBB()
{
boundingBox.left = (int)x - 45;
boundingBox.width = 90;
boundingBox.top = (int)y - 45;
boundingBox.height = 90;
}
void SpiderWebEntity::collideMapRight()
{
collideWall();
}
void SpiderWebEntity::collideMapLeft()
{
collideWall();
}
void SpiderWebEntity::collideMapTop()
{
collideWall();
}
void SpiderWebEntity::collideMapBottom()
{
collideWall();
}
void SpiderWebEntity::collideWall()
{
velocity.x = 0.0f;
velocity.y = 0.0f;
}
void SpiderWebEntity::collideWithEnemy(EnemyEntity* enemyEntity)
{
if (enemyEntity->getEnemyType() != EnemyTypeSpiderLittle_invocated
&& enemyEntity->getEnemyType() != EnemyTypeSpiderLittle
&& enemyEntity->getEnemyType() != EnemyTypeSpiderGiant
&& enemyEntity->getEnemyType() != EnemyTypeSpiderTarantula
&& enemyEntity->getEnemyType() != EnemyTypeSpiderTarantula_invocated
&& enemyEntity->getEnemyType() != EnemyTypeSpiderEgg
&& enemyEntity->getEnemyType() != EnemyTypeSpiderEgg_invocated
&& enemyEntity->getEnemyType() != EnemyTypeSpiderWeb)
{
if (!enemyEntity->isSpecialStateActive(SpecialStateSlow))
{
enemyEntity->setSpecialState(SpecialStateSlow, true, 0.15f, 0.25f, 0.0f);
hurt(getHurtParams(2, ShotTypeStandard, 0, false, SourceTypeMelee, enemyEntity->getEnemyType(), false));
}
}
}
void SpiderWebEntity::drop()
{
}
void SpiderWebEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (!isFromPlayer && playerEntity != NULL && !playerEntity->isDead())
{
if (!playerEntity->isSpecialStateActive(SpecialStateSlow))
{
playerEntity->setSpecialState(SpecialStateSlow, true, 0.1f, 0.33f, 0.0f);
// TODO
hurt(getHurtParams(2, ShotTypeStandard, 0, false, SourceTypeMelee, EnemyTypeNone, false));
}
}
else if (!isFromPlayer && boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
EnemyEntity::collideWithBolt(boltEntity);
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENEMY && entity->getType() <= ENTITY_ENEMY_MAX)
{
if (this != entity)
{
EnemyEntity* enemyEntity = static_cast<EnemyEntity*>(entity);
if (enemyEntity->canCollide()) collideWithEnemy(enemyEntity);
}
}
}
}
}
diff --git a/src/VampireEntity.cpp b/src/VampireEntity.cpp
index 695dda2..1957cf1 100644
--- a/src/VampireEntity.cpp
+++ b/src/VampireEntity.cpp
@@ -1,806 +1,806 @@
#include "VampireEntity.h"
#include "BatEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextMapper.h"
#include <iostream>
#include <sstream>
const int VAMPIRE_HP = 1500;
const int VAMPIRE_DAMAGE = 12;
const float VAMPIRE_FLYING_DELAY = 1.2f;
const float VAMPIRE_BAT_DELAY = 0.225f;
const float VAMPIRE_CONFUSION_DELAY = 2.5f;
const float VAMPIRE_TRANSFORM_DELAY = 0.4f;
const float VAMPIRE_CRY_DELAY = 6.0f;
const float VAMPIRE_MOVE_COUNTER_MAX = 2;
const float VAMPIRE_DYING_TIME = 4.2f;
const int FORM_MAN = 0;
const int FORM_BAT = 1;
VampireEntity::VampireEntity(float myx, float myy)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_VAMPIRE), myx, myy)
{
width = 96;
height = 96;
creatureSpeed = 150;
hp = VAMPIRE_HP;
hpDisplay = VAMPIRE_HP;
hpMax = VAMPIRE_HP;
meleeDamages = VAMPIRE_DAMAGE;
shadowFrame = 30;
bodyFrame = 0;
type = ENTITY_ENEMY_BOSS;
//deathFrame = FRAME_CORPSE_CYCLOP;
batSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_VAMPIRE_BAT));
frame = 0;
if (game().getPlayerPosition().x > x) isMirroring = true;
sprite.setOrigin(48.0f, 74.0f);
imagesProLine = 6;
state = 0;
timer = 2.0f;
age = -1.0f;
enemyType = EnemyTypeVampire;
formState = FORM_MAN;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
- resistance[ResistanceRecoil] = ResistanceVeryHigh;
+ resistance[ResistanceRepulsion] = ResistanceVeryHigh;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
x = GAME_WIDTH * 0.5f;
y = GAME_HEIGHT * 0.5f;
if (game().getPlayer()->getX() < 2 * TILE_WIDTH)
{
x = GAME_WIDTH - 2.5f * TILE_WIDTH;
targetPos = 4;
}
else if (game().getPlayer()->getX() > GAME_WIDTH - 2 * TILE_WIDTH)
{
x = 2.5f * TILE_WIDTH;
targetPos = 6;
}
else if (game().getPlayer()->getY() < 2 * TILE_HEIGHT)
{
y = GAME_HEIGHT - 2.5f * TILE_HEIGHT;
targetPos = 2;
}
else if (game().getPlayer()->getY() > GAME_HEIGHT - 2 * TILE_HEIGHT)
{
y = 2.5f * TILE_HEIGHT;
targetPos = 8;
}
moveCounter = 0;
}
int VampireEntity::getHealthLevel()
{
int healthLevel = 0;
if (hp <= hpMax * 0.25) healthLevel = 3;
else if (hp <= hpMax * 0.5) healthLevel = 2;
else if (hp <= hpMax * 0.75) healthLevel = 1;
return healthLevel;
}
void VampireEntity::computeStates(float delay)
{
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0) // waiting bef hypnose
{
state = 1; // hypnose
timer = 1.6f;
game().getPlayer()->setSpecialState(SpecialStateConfused, true, VAMPIRE_CONFUSION_DELAY, VAMPIRE_CONFUSION_DELAY, 0.0f);
}
else if (state == 1) // hypnose
{
state = 2;
timer = 0.5f;
}
else if (state == 2) // waiting before laughing
{
state = 3; // laughing
timer = 1.3f; // 3.0f;
SoundManager::getInstance().playSound(SOUND_VAMPIRE_LAUGHING);
}
else if (state == 3) // laughing
{
state = 4;
timer = 0.1f;
}
else if (state == 4) // to bat cloud
{
if (getHealthLevel() > 1 && moveCounter <= 0)
{
state = 8;
timer = VAMPIRE_TRANSFORM_DELAY;
formState = FORM_BAT;
moveCounter = VAMPIRE_MOVE_COUNTER_MAX;
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BOLT);
}
else
{
state = 5;
timer = VAMPIRE_TRANSFORM_DELAY;
moveCounter--;
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BOLT);
}
}
else if (state == 5) // to bat cloud
{
state = 6;
frame = 10;
timer = VAMPIRE_FLYING_DELAY;
batTimer = VAMPIRE_BAT_DELAY;
xSource = x;
ySource = y;
if (targetPos == 4 || targetPos == 6)
{
targetPos = rand() % 2 == 0 ? 2 : 8;
}
else
{
targetPos = rand() % 2 == 0 ? 4 : 6;
}
}
else if (state == 6) // vampire flying in the cloud
{
state = 7;
timer = VAMPIRE_TRANSFORM_DELAY;
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BOLT);
}
else if (state == 7) // cloud to vampire
{
state = 0;
timer = 0.5f;
}
else if (state == 8) // vampire to cloud < 50% HP
{
state = 9; // cloud to center
frame = 10;
timer = VAMPIRE_FLYING_DELAY;
batTimer = VAMPIRE_BAT_DELAY;
xSource = x;
ySource = y;
targetPos = 5;
if (hp <= hpMax * 0.1f)
{
numberOfRays = 6;
raySpeedFactor = 35.0f;
}
else if (hp <= hpMax * 0.2f)
{
numberOfRays = 5;
raySpeedFactor = 35.0f;
}
else if (hp <= hpMax * 0.3f)
{
numberOfRays = 5;
raySpeedFactor = 30.0f;
}
else if (hp <= hpMax * 0.4f)
{
numberOfRays = 4;
raySpeedFactor = 30.0f;
}
else
{
numberOfRays = 4;
raySpeedFactor = 20.0f;
}
}
else if (state == 9) // cloud to center
{
state = 10; // transform to giant bat
timer = VAMPIRE_TRANSFORM_DELAY;
}
else if (state == 10) // transform to giant bat
{
state = 11; // giant bat
for (int i = 0; i < 10; i++)
{
game().generateStar(sf::Color(200, 0, 200), x - 60 + rand() % 121, y - 60 + rand() % 121);
game().generateStar(sf::Color(0, 0, 0), x - 60 + rand() % 121, y - 60 + rand() % 121);
}
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BAT);
timer = 1.0f;
}
else if (state == 11) // giant bat waiting
{
state = 12; // to cry
timer = VAMPIRE_CRY_DELAY;
}
else if (state == 12) // cry !
{
state = 13; // giant bat waiting
timer = VAMPIRE_TRANSFORM_DELAY;
for (int i = 0; i < 10; i++)
{
game().generateStar(sf::Color(200, 0, 200), x - 40 + rand() % 81, y - 40 + rand() % 81);
game().generateStar(sf::Color(0, 0, 0), x - 40 + rand() % 81, y - 40 + rand() % 81);
}
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BAT);
}
else if (state == 13) // cry !
{
state = 6;
frame = 10;
timer = VAMPIRE_FLYING_DELAY;
batTimer = VAMPIRE_BAT_DELAY;
xSource = x;
ySource = y;
if (targetPos == 4 || targetPos == 6)
{
targetPos = rand() % 2 == 0 ? 2 : 8;
}
else
{
targetPos = rand() % 2 == 0 ? 4 : 6;
}
}
else
{
state = 0;
timer = 5.0f;
}
}
}
void VampireEntity::animate(float delay)
{
if (isAgonising)
{
timer += delay;
if (timer > VAMPIRE_DYING_TIME)
{
dying();
VampireDeadEntity* corpse = new VampireDeadEntity(x, y);
corpse->setMirroring(isMirroring);
}
return;
}
EnemyEntity::animate(delay);
if (isAgonising) return;
computeStates(delay);
if (state == 0)
{
if (timer < 0.2f) frame = 1;
else frame = 0;
}
else if (state == 1) // hypnose
{
frame = 2;
}
else if (state == 2 || state == 4) // waiting
{
frame = 0;
}
else if (state == 3) // laughing
{
frame = 3 + ((int)(age * 7.0f)) % 2;
}
else if (state == 5 || state == 8) // to bat cloud
{
sprite.setColor(sf::Color(255, 255, 255, 255));
if (timer > 0.2f)
{
frame = 0;
float fade = (VAMPIRE_TRANSFORM_DELAY - timer) / VAMPIRE_TRANSFORM_DELAY;
sprite.setColor(sf::Color(255 - fade * 250, 255 - fade * 250, 255 - fade * 250, 255));
isMirroring = game().getPlayer()->getX() > x;
}
else if (timer > 0.15f) frame = 6;
else if (timer > 0.1f) frame = 7;
else if (timer > 0.05f) frame = 8;
else frame = 9;
}
else if (state == 7 || state == 10) // to bat cloud
{
sprite.setColor(sf::Color(255, 255, 255, 255));
if (timer < 0.2f)
{
frame = 0;
float fade = timer * 4;
sprite.setColor(sf::Color(255 - fade * 250, 255 - fade * 250, 255 - fade * 250, 255));
isMirroring = game().getPlayer()->getX() > x;
}
else if (timer < VAMPIRE_TRANSFORM_DELAY - 0.15f) frame = 6;
else if (timer < VAMPIRE_TRANSFORM_DELAY - 0.1f) frame = 7;
else if (timer < VAMPIRE_TRANSFORM_DELAY - 0.05f) frame = 8;
else frame = 9;
}
else if (state == 6 || state == 9)
{
calculatePosition();
batTimer -= delay;
// bat generation
if (batTimer <= 0.0f)
{
batTimer += VAMPIRE_BAT_DELAY;
isMirroring = !isMirroring;
BatEntity* bat;
if (hp <= hpMax * 0.25f)
bat = new BatEntity(x, y, BatSkeleton, true);
else
bat = new BatEntity(x, y, BatStandard, true);
bat->setAge(0.0f);
}
// particules
for (int i = 0; i < 2; i++)
{
SpriteEntity* particle = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_VAMPIRE), x, y - 16, 96, 96);
particle->setFading(true);
particle->setImagesProLine(6);
particle->setZ(10);
particle->setLifetime(0.3f + 0.1f * (rand() % 10));
particle->setVelocity(Vector2D(12.0f));
particle->setType(ENTITY_EFFECT);
particle->setFrame(11 + 6 * i);
//particle->setFading(true);
particle->setShrinking(true);
particle->setX(x - 20 + rand()% 41);
particle->setY(y - 40 + rand()% 41);
}
for (int i = 0; i < 2; i++)
{
SpriteEntity* particle = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_VAMPIRE), x, y - 16, 96, 96);
particle->setFading(true);
particle->setImagesProLine(6);
particle->setZ(11);
//particle->setLifetime(0.9f);
particle->setLifetime(0.3f + 0.1f * (rand() % 10));
particle->setVelocity(Vector2D(12.0f));
particle->setType(ENTITY_EFFECT);
particle->setFrame(23 + 6 * i);
//particle->setFading(true);
particle->setShrinking(true);
particle->setRenderAdd();
particle->setX(x - 20 + rand()% 41);
particle->setY(y - 40 + rand()% 41);
}
}
else if (state == 12)
{
x = GAME_WIDTH / 2;
y = GAME_HEIGHT / 2;
testRaysCollision();
}
if (state != 5 && state != 7) isMirroring = game().getPlayer()->getX() > x;
z = y + 16;
}
void VampireEntity::calculatePosition()
{
float xTarget, yTarget;
if (targetPos == 4) xTarget = 2.5f * TILE_WIDTH;
else if (targetPos == 6) xTarget = GAME_WIDTH - 2.5f * TILE_WIDTH;
else xTarget = GAME_WIDTH * 0.5f;
if (targetPos == 8) yTarget = 2.5f * TILE_HEIGHT;
else if (targetPos == 2) yTarget = GAME_HEIGHT - 2.5f * TILE_HEIGHT;
else yTarget = GAME_HEIGHT * 0.5f;
x = xSource + (VAMPIRE_FLYING_DELAY - timer) / VAMPIRE_FLYING_DELAY * (xTarget - xSource);
y = ySource + (VAMPIRE_FLYING_DELAY - timer) / VAMPIRE_FLYING_DELAY * (yTarget - ySource);
}
int VampireEntity::hurt(StructHurt hurtParam)
{
if (state == 6 || state == 8 ||state == 9) armor = 1.0f;
else armor = 0.0f;
int result = EnemyEntity::hurt(hurtParam);
return result;
}
void VampireEntity::prepareDying()
{
// Giant bat ?
if (state == 11 || state ==12)
{
for (int i = 0; i < 10; i++)
{
game().generateStar(sf::Color(200, 0, 200), x - 40 + rand() % 81, y - 40 + rand() % 81);
game().generateStar(sf::Color(0, 0, 0), x - 40 + rand() % 81, y - 40 + rand() % 81);
}
SoundManager::getInstance().playSound(SOUND_VAMPIRE_TRANSFORM_BAT);
}
timer = 0.0f;
sprite.setOrigin(0.0f, 0.0f);
isAgonising = true;
SoundManager::getInstance().playSound(SOUND_VAMPIRE_DYING);
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
EnemyEntity* entity = dynamic_cast<EnemyEntity*>(e);
if (entity != NULL)
{
if (entity->getHp() > 0)
{
entity->hurt(getHurtParams(entity->getHp(), ShotTypeStandard, 0, false, SourceTypeMelee, enemyType, false));
}
}
}
type = ENTITY_ENEMY_NC;
}
void VampireEntity::calculateBB()
{
if (state >= 11)
{
boundingBox.left = (int)x - 50;
boundingBox.width = 100;
boundingBox.top = (int)y - 50;
boundingBox.height = 100;
}
else
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 16;
boundingBox.height = 32;
}
}
void VampireEntity::drop()
{
dropItem(ItemBossHeart);
}
void VampireEntity::render(sf::RenderTarget* app)
{
if (isAgonising)
{
sprite.setPosition(x - 48, y - 74);
if (isMirroring) sprite.setTextureRect(sf::IntRect(1 * width, 2 * height, -width, height));
else sprite.setTextureRect(sf::IntRect(0 * width, 2 * height, width, height));
app->draw(sprite);
float burnHeight = timer < 4.0f ? height * timer / 4.0f : height;
if (isMirroring) sprite.setTextureRect(sf::IntRect(2 * width, 2 * height, -width, burnHeight));
else sprite.setTextureRect(sf::IntRect(width, 2 * height, width, burnHeight));
app->draw(sprite);
if (isMirroring) sprite.setTextureRect(sf::IntRect(2 * width, 2 * height, -width, height));
else sprite.setTextureRect(sf::IntRect(1 * width, 2 * height, width, height));
// fire
if (timer > 0.1f && timer < 3.8f)
{
sf::Sprite burn;
burn.setTexture(*ImageManager::getInstance().getImage(IMAGE_VAMPIRE));
if (isMirroring) burn.setTextureRect(sf::IntRect(5 * width, 2 * height + burnHeight - 4, -width, 6));
else burn.setTextureRect(sf::IntRect(4 * width, 2 * height + burnHeight - 4, width, 6));
burn.setPosition(x - 48, y - 74 - 4 + burnHeight);
app->draw(burn);
if (isMirroring) burn.setTextureRect(sf::IntRect(4 * width, 2 * height + burnHeight - 2, -width, 4));
else burn.setTextureRect(sf::IntRect(3 * width, 2 * height + burnHeight - 2, width, 4));
burn.setPosition(x - 48, y - 74 - 2 + burnHeight);
app->draw(burn);
float fade = (cos(12.0f * game().getAbsolutTime()) + 1.0f) * 0.5f;
burn.setColor(sf::Color(255, 255, 255, 100 * fade));
app->draw(burn, sf::BlendAdd);
}
// cone
sf::Sprite cone;
cone.setTexture(*ImageManager::getInstance().getImage(IMAGE_LIGHT_CONE));
cone.setPosition(x - 68, y - 600);
int fade = 200;
if (timer < 0.2f) fade = timer * 1000;
else if (timer > VAMPIRE_DYING_TIME - 0.5f) fade = (VAMPIRE_DYING_TIME - timer) * 400;
if (fade < 0) fade = 0;
cone.setColor(sf::Color(255, 255, 255, fade));
app->draw(cone, sf::BlendAdd);
}
else
{
if (state == 11 || state == 12) // giant bat
{
int oldBodyFrame = bodyFrame;
bodyFrame = (int)(age * 18) % 9;
if (oldBodyFrame == 6 && bodyFrame == 7)
SoundManager::getInstance().playSound(SOUND_VAMPIRE_FLAP);
batSprite.setTextureRect(sf::IntRect(418 * (bodyFrame % 3), 342 * (bodyFrame / 3), 418, 342));
batSprite.setPosition(x - 209, y - 200);
batSprite.setColor(sprite.getColor());
app->draw(batSprite);
if (state == 11)
{
frame = (int)(age * 4) % 9;
if (frame >= 5) frame = 8 - frame;
sprite.setTextureRect(sf::IntRect(width * frame, 3 * height, width, height));
if (timer < 1.0f && (int)(timer * 20) % 3 == 0) renderRays(app, false);
}
else if (state == 12)
{
// rays
renderRays(app, false);
// head
if (timer < 0.05f) frame = 1;
else if (timer < 0.1f) frame = 2;
else if (timer < 0.15f) frame = 3;
else if (timer < VAMPIRE_CRY_DELAY - 0.3f) frame = 4;
else if (timer < VAMPIRE_CRY_DELAY - 0.2f) frame = 3;
else if (timer < VAMPIRE_CRY_DELAY - 0.1f) frame = 2;
else frame = 1;
sprite.setTextureRect(sf::IntRect(width * frame, 4 * height, width, height));
}
sprite.setPosition(x + cosf(age * 2) * 2, y + sinf(age * 2) * 6);
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
else
{
EnemyEntity::render(app);
if (state == 1) // hypnose
{
sf::Sprite eye;
eye.setOrigin(6, 6);
eye.setTexture(*ImageManager::getInstance().getImage(IMAGE_VAMPIRE));
eye.setTextureRect(sf::IntRect(5 * width, 0, 12, 12));
eye.setRotation(age * 500);
if (isMirroring) eye.setPosition(x + 10, y - 44);
else eye.setPosition(x - 10, y - 44);
app->draw(eye);
float fade = (cos(8.0f * game().getAbsolutTime()) + 1.0f) * 0.5f;
eye.setColor(sf::Color(255, 255, 255, 255 * fade));
app->draw(eye, sf::BlendAdd);
if (isMirroring) eye.setPosition(x - 4, y - 44);
else eye.setPosition(x + 4, y - 44);
eye.setColor(sf::Color(255, 255, 255, 255 ));
app->draw(eye);
eye.setColor(sf::Color(255, 255, 255, 255 * fade));
app->draw(eye, sf::BlendAdd);
}
}
renderLifeBar(app, tools::getLabel("enemy_vampire"));
}
}
void VampireEntity::renderRays(sf::RenderTarget* app, bool isGhost)
{
// rays
sf::RectangleShape ray(sf::Vector2f(500, 4));
ray.setOrigin(0, 2);
if (isGhost)
{
ray.setFillColor(sf::Color(128, 50, 50, 200));
ray.setOutlineColor(sf::Color(128, 50, 50, 100));
}
else
{
ray.setFillColor(sf::Color(255, 50, 50));
ray.setOutlineColor(sf::Color(255, 50, 50, 128));
}
ray.setOutlineThickness(1);
ray.setPosition(GAME_WIDTH / 2, GAME_HEIGHT / 2);
sf::RectangleShape rayLittle(sf::Vector2f(500, 2));
rayLittle.setOrigin(0, 1);
if (isGhost)
{
rayLittle.setFillColor(sf::Color(128, 50, 50, 100));
rayLittle.setOutlineColor(sf::Color(128, 50, 50, 50));
}
else
{
rayLittle.setFillColor(sf::Color(255, 50, 50, 150));
rayLittle.setOutlineColor(sf::Color(255, 50, 50, 75));
}
rayLittle.setOutlineThickness(1);
rayLittle.setPosition(GAME_WIDTH / 2, GAME_HEIGHT / 2);
float rayAngle = age * raySpeedFactor;
for (int i = 0; i < numberOfRays; i++)
{
ray.setRotation(rayAngle + i * 360 / numberOfRays);
app->draw(ray);
rayLittle.setRotation(ray.getRotation() + 3);
app->draw(rayLittle);
rayLittle.setRotation(ray.getRotation() - 3);
app->draw(rayLittle);
}
if (!isGhost)
SoundManager::getInstance().playSound(SOUND_VAMPIRE_SONIC_RAY, false);
}
void VampireEntity::testRaysCollision()
{
PlayerEntity* player = game().getPlayer();
if (player->canCollide() && player->getHp() > 0)
{
float rayAngle = age * raySpeedFactor;
Vector2D a1(GAME_WIDTH / 2, GAME_HEIGHT / 2);
Vector2D b1(player->getBoundingBox().left, player->getBoundingBox().top);
Vector2D b2(player->getBoundingBox().left + player->getBoundingBox().width, player->getBoundingBox().top);
Vector2D b3(player->getBoundingBox().left + player->getBoundingBox().width, player->getBoundingBox().top + player->getBoundingBox().height);
Vector2D b4(player->getBoundingBox().left, player->getBoundingBox().top + player->getBoundingBox().height);
for (int i = 0; i < numberOfRays; i++)
{
float currentAngle = rayAngle + i * 360 / numberOfRays;
Vector2D a2(GAME_WIDTH / 2 + 500 * cosf(currentAngle / 57.3f),
GAME_HEIGHT / 2 + 500 * sinf(currentAngle / 57.3f));
if (intersectsSegments(a1, a2, b1, b2)
|| intersectsSegments(a1, a2, b2, b3)
|| intersectsSegments(a1, a2, b3, b4)
|| intersectsSegments(a1, a2, b4, b1))
{
if (player->hurt(getHurtParams(8, ShotTypeStandard, 0, false, SourceTypeMelee, EnemyTypeVampire, false)) > 0)
{
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_VAMPIRE_PART), player->getX(), player->getY());
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
}
}
}
}
void VampireEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 450.0f);
- targetEntity->giveRecoil(true, recoilVector, 0.5f);
+ targetEntity->giveRepulsion(true, recoilVector, 0.5f);
}
}
void VampireEntity::generateBats(int batFrame)
{
SpriteEntity* spriteStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_VAMPIRE),
x - 50 + rand() % 101, y - 50 + rand() % 101, 96, 96, 6);
//spriteStar->setScale(0.8f, 0.8f);
spriteStar->setFrame(batFrame);
spriteStar->setZ(1000.0f);
//spriteStar->setSpin(-100 + rand()%200);
spriteStar->setVelocity(Vector2D(30 + rand()%60));
spriteStar->setWeight(-150);
spriteStar->setFading(true);
spriteStar->setAge(-0.8f);
spriteStar->setLifetime(0.1f + (rand() % 100) * 0.003f );
spriteStar->setType(ENTITY_EFFECT);
}
////////////////////// DEAD VAMPIRE /////////////////
VampireDeadEntity::VampireDeadEntity(float myx, float myy)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_VAMPIRE), myx, myy)
{
width = 96;
height = 96;
creatureSpeed = 0;
hp = 80;
hpMax = 80;
shadowFrame = 30;
type = ENTITY_ENEMY_NC;
deathFrame = FRAME_CORPSE_VAMPIRE;
dyingFrame = 14;
displayDamage = false;
frame = 13;
sprite.setOrigin(48.0f, 74.0f);
imagesProLine = 6;
bloodColor = BloodNone;
enemyType = EnemyTypeVampireDead;
resistance[ResistanceFrozen] = ResistanceImmune;
resistance[ResistancePoison] = ResistanceImmune;
canExplode = false;
}
void VampireDeadEntity::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 16;
boundingBox.height = 32;
}
void VampireDeadEntity::drop()
{
dropItem(ItemGoldCoin);
}
void VampireDeadEntity::animate(float delay)
{
EnemyEntity::animate(delay);
z = y + 16;
}
void VampireDeadEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(targetEntity);
if (playerEntity != NULL && !playerEntity->isDead())
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), 100.0f);
- targetEntity->giveRecoil(false, recoilVector, 0.1f);
+ targetEntity->giveRepulsion(false, recoilVector, 0.1f);
}
}
void VampireDeadEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead()) inflictsRecoilTo(playerEntity);
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
collideWithBolt(boltEntity);
}
}
}
}
diff --git a/src/WitchBlastGame.cpp b/src/WitchBlastGame.cpp
index aa9d606..35ec547 100644
--- a/src/WitchBlastGame.cpp
+++ b/src/WitchBlastGame.cpp
@@ -1,7812 +1,7927 @@
/** This file is part of Witch Blast.
*
* Witch Blast is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Witch Blast is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WitchBlastGame.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/TileMapEntity.h"
#include "DungeonMap.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "sfml_game/EntityManager.h"
#include "Constants.h"
#include "RatEntity.h"
#include "BlackRatEntity.h"
#include "GreenRatEntity.h"
#include "KingRatEntity.h"
#include "CyclopsEntity.h"
#include "GiantSpiderEntity.h"
#include "GiantSlimeEntity.h"
#include "ButcherEntity.h"
#include "BatEntity.h"
#include "ImpEntity.h"
#include "SlimeEntity.h"
#include "LargeSlimeEntity.h"
#include "PumpkinEntity.h"
#include "ChestEntity.h"
#include "EvilFlowerEntity.h"
#include "BubbleEntity.h"
#include "ItemEntity.h"
#include "WitchEntity.h"
#include "CauldronEntity.h"
#include "SnakeEntity.h"
#include "GhostEntity.h"
#include "ZombieEntity.h"
#include "ZombieDarkEntity.h"
#include "BogeymanEntity.h"
#include "LittleSpiderEntity.h"
#include "SpiderEggEntity.h"
#include "FranckyEntity.h"
#include "VampireEntity.h"
#include "ObstacleEntity.h"
#include "ArtefactDescriptionEntity.h"
#include "PnjEntity.h"
#include "TextEntity.h"
#include "StandardRoomGenerator.h"
#include "Scoring.h"
#include "MessageGenerator.h"
#include "TextMapper.h"
#include "SlimePetEntity.h"
#include "SausageEntity.h"
#include "FairyEntity.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <ctime>
#include <iomanip>
#include <algorithm>
//#define LEVEL_TEST_MODE
#ifdef ONLINE_MODE
#include "OnlineScoring.h"
#endif // ONLINE_MODE
const float PORTRAIT_DIAPLAY_TIME = 5.0f;
const float ENDING_TIMER = 8.0f;
const float BONUS_TIMER = 4.0f;
const unsigned int ACHIEV_LINES = 3;
const int VolumeModifier = 55;
static std::string intToString(int n)
{
std::ostringstream oss;
oss << n;
return oss.str();
}
static std::string keyToString(sf::Keyboard::Key key)
{
std::string s = "Unknown";
if (key >= sf::Keyboard::A && key <= sf::Keyboard::Z)
{
char c = 'A' + key - sf::Keyboard::A;
std::ostringstream oss;
oss << c;
s = oss.str();
}
else if (key >= sf::Keyboard::Num0 && key <= sf::Keyboard::Num9)
{
char c = '0' + key - sf::Keyboard::Num0;
std::ostringstream oss;
oss << c;
s = oss.str();
}
else if (key >= sf::Keyboard::Numpad0 && key <= sf::Keyboard::Numpad9)
{
char c = '0' + key - sf::Keyboard::Num0;
std::ostringstream oss;
oss << c << " (numpad)";
s = oss.str();
}
else if (key >= sf::Keyboard::F1 && key <= sf::Keyboard::F15)
{
std::ostringstream oss;
oss << "F" << (int)key - (int)sf::Keyboard::F1;
s = oss.str();
}
else if (key == sf::Keyboard::Escape) s = "Escape";
else if (key == sf::Keyboard::LControl) s = "Left Control";
else if (key == sf::Keyboard::LShift) s = "Left Shift";
else if (key == sf::Keyboard::LAlt) s = "Left Alt";
else if (key == sf::Keyboard::LSystem) s = "Left System";
else if (key == sf::Keyboard::RControl) s = "Right Control";
else if (key == sf::Keyboard::RShift) s = "Right Shift";
else if (key == sf::Keyboard::RAlt) s = "Right Alt";
else if (key == sf::Keyboard::RSystem) s = "Right System";
else if (key == sf::Keyboard::Space) s = "Space";
else if (key == sf::Keyboard::Tab) s = "Tab";
else if (key == sf::Keyboard::Left) s = "Left arrow";
else if (key == sf::Keyboard::Right) s = "Right arrow";
else if (key == sf::Keyboard::Up) s = "Up arrow";
else if (key == sf::Keyboard::Down) s = "Down arrow";
else if (key == sf::Keyboard::LBracket) s = "[";
else if (key == sf::Keyboard::RBracket) s = "]";
else if (key == sf::Keyboard::SemiColon) s = ";";
else if (key == sf::Keyboard::Comma) s = ",";
else if (key == sf::Keyboard::Period) s = ".";
else if (key == sf::Keyboard::Quote) s = "Quote";
else if (key == sf::Keyboard::Slash) s = "/";
else if (key == sf::Keyboard::BackSlash) s = "\\";
else if (key == sf::Keyboard::Tilde) s = "~";
else if (key == sf::Keyboard::Equal) s = "=";
else if (key == sf::Keyboard::Dash) s = "_";
else if (key == sf::Keyboard::Return) s = "Return";
else if (key == sf::Keyboard::BackSpace) s = "BackSpace";
else if (key == sf::Keyboard::PageUp) s = "Page Up";
else if (key == sf::Keyboard::PageDown) s = "Page Down";
else if (key == sf::Keyboard::End) s = "End";
else if (key == sf::Keyboard::Home) s = "Home";
else if (key == sf::Keyboard::Insert) s = "Insert";
else if (key == sf::Keyboard::Delete) s = "Delete";
else if (key == sf::Keyboard::Add) s = "+";
else if (key == sf::Keyboard::Subtract) s = "-";
else if (key == sf::Keyboard::Multiply) s = "*";
else if (key == sf::Keyboard::Divide) s = "/";
else if (key == sf::Keyboard::Pause) s = "Pause";
else if (key == sf::Keyboard::KeyCount) s = "Key Count";
return s;
}
std::map<EnumWorldEvents, EnumMessages> eventToMessage =
{
{ EventMeetRatsOrBats, MsgInfoRatsBats },
{ EventMeetSnakes, MsgInfoSnakes },
{ EventMeetWitches, MsgInfoWitches },
{ EventGetCoin, MsgInfoGold },
{ EventGetFamiliar, MsgInfoFamiliar },
{ EventBeingHurted, MsgTutoHeal },
{ EventFindShop, MsgTutoShops },
{ EventFindBossDoor, MsgTutoBossDoor },
{ EventFindChallengeDoor, MsgTutoChallengeDoor },
{ EventFindTemple, MsgTutoTemple },
{ EventGetItem, MsgTutoItems },
{ EventGetSpecialShot, MsgTutoShots },
{ EventGetSpell, MsgTutoSpell },
{ EventAchievement, MsgTutoAchievements },
{ EventConsumable, MsgTutoConsumables },
{ EventPotion, MsgTutoPotions },
};
// author: AFS
// source: https://github.com/LaurentGomila/SFML/wiki/Source:-Letterbox-effect-using-a-view
static sf::View getLetterboxView(sf::View view, int windowWidth, int windowHeight)
{
// Compares the aspect ratio of the window to the aspect ratio of the view,
// and sets the view's viewport accordingly in order to archieve a letterbox effect.
// A new view (with a new viewport set) is returned.
float windowRatio = windowWidth / (float) windowHeight;
float viewRatio = view.getSize().x / (float) view.getSize().y;
float sizeX = 1;
float sizeY = 1;
float posX = 0;
float posY = 0;
bool horizontalSpacing = true;
if (windowRatio < viewRatio)
horizontalSpacing = false;
// If horizontalSpacing is true, the black bars will appear on the left and right side.
// Otherwise, the black bars will appear on the top and bottom.
if (horizontalSpacing)
{
sizeX = viewRatio / windowRatio;
posX = (1 - sizeX) / 2.0;
}
else
{
sizeY = windowRatio / viewRatio;
posY = (1 - sizeY) / 2.0;
}
view.setViewport( sf::FloatRect(posX, posY, sizeX, sizeY) );
return view;
}
static sf::View getFullScreenLetterboxView(sf::View view, int clientWidth, int clientHeight)
{
sf::View returnView(sf::FloatRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
float clientRatio = clientWidth / (float) clientHeight;
float viewRatio = view.getSize().x / (float) view.getSize().y;
float sizeX = 1;
float sizeY = 1;
float posX = 0;
float posY = 0;
bool horizontalSpacing = true;
if (clientRatio > viewRatio)
horizontalSpacing = false;
// If horizontalSpacing is true, the black bars will appear on the left and right side.
// Otherwise, the black bars will appear on the top and bottom.
if (horizontalSpacing)
{
sizeX = clientRatio / viewRatio;
posX = (1 - sizeX) / 2.0;
}
else
{
sizeY = viewRatio / clientRatio;
posY = (1 - sizeY) / 2.0;
}
returnView.setViewport( sf::FloatRect(posX, posY, sizeX, sizeY) );
return returnView;
}
+static enemyTypeEnum enemyStringToEnum(std::string enemy)
+{
+ enemyTypeEnum result = NB_ENEMY;
+ for (int i = 0; i < NB_ENEMY; i++)
+ {
+ if (enemyString[i].compare(enemy) == 0)
+ result = (enemyTypeEnum)i;
+ }
+ return result;
+}
+
+static enumAchievementType achievementStringToEnum(std::string achievString)
+{
+ enumAchievementType result = NB_ACHIEVEMENTS;
+ for (int i = 0; i < NB_ACHIEVEMENTS; i++)
+ {
+ if (achievements[i].label.compare(achievString) == 0)
+ result = achievements[i].id;
+ }
+ return result;
+}
+
+static std::string achievementEnumToString(enumAchievementType achiev)
+{
+ std::string result = "";
+ for (int i = 0; i < NB_ACHIEVEMENTS; i++)
+ {
+ if (achievements[i].id == achiev)
+ result = achievements[i].label;
+ }
+ return result;
+}
+
+static EnumMessages messageStringToEnum(std::string msgString)
+{
+ EnumMessages result = NB_MESSAGES;
+ for (int i = 0; i < NB_MESSAGES; i++)
+ {
+ if (msgMap[(EnumMessages)i].key.compare(msgString) == 0)
+ result = (EnumMessages)i;
+ }
+ return result;
+}
+
+static std::string messageEnumToString(EnumMessages msg)
+{
+ if (msg < NB_MESSAGES) return msgMap[msg].key;
+ else return "";
+}
+
namespace
{
WitchBlastGame* gameptr;
}
WitchBlastGame::WitchBlastGame()
{
gameptr = this;
gameFromSaveFile = false;
configureFromFile();
if (parameters.vsync == false)
{
//app->setVerticalSyncEnabled(false);
app->setFramerateLimit(60);
}
// Fullscreen ?
if (parameters.fullscreen)
{
create(SCREEN_WIDTH, SCREEN_HEIGHT, APP_NAME + " V" + APP_VERSION, true, parameters.vsync);
sf::View view = app->getDefaultView();
view = getFullScreenLetterboxView( view, SCREEN_WIDTH, SCREEN_HEIGHT );
app->setView(view);
}
else
{
create(SCREEN_WIDTH, SCREEN_HEIGHT, APP_NAME + " V" + APP_VERSION, false, parameters.vsync);
}
// init current music
currentStandardMusic = 0;
// loading resources
const char *const images[] =
{
"media/player_0.png", "media/player_1.png",
"media/player_2.png",
"media/bolt.png", "media/tiles.png",
"media/rat.png", "media/minimap.png",
"media/map_background.png",
"media/items.png", "media/items_equip.png",
"media/items.png", "media/items_equip.png",
"media/chest.png", "media/bat.png",
"media/evil_flower.png", "media/slime.png",
"media/imp.png", "media/spider_egg.png",
"media/spider_web.png", "media/little_spider.png",
"media/bubble.png", "media/eyeball.png",
"media/witch.png",
"media/cauldron.png", "media/snake.png",
"media/pumpkin.png", "media/ghost.png",
"media/zombie.png", "media/bogeyman.png",
"media/sausage.png",
"media/butcher.png", "media/giant_slime.png",
"media/king_rat.png", "media/cyclops.png",
"media/giant_spider.png", "media/francky.png",
"media/vampire.png", "media/vampire_bat.png",
"media/vampire_part.png",
"media/blood.png",
"media/corpses.png", "media/corpses_big.png",
"media/star.png", "media/star2.png",
"media/hurt_impact.png",
"media/interface.png", "media/hud_shots.png",
"media/explosion.png", "media/keys_qwer.png",
"media/keys_azer.png",
"media/night.png", "media/title.png",
"media/overlay_00.png", "media/light_cone.png",
"media/divinity.png",
"media/pnj.png", "media/fairy.png",
"media/key_area.png",
"media/ui_life.png", "media/ui_mana.png",
"media/ui_spells.png", "media/ui_message.png",
"media/ui_top_layer.png", "media/ui_achiev.png",
"media/fog.png", "media/title_animation.png",
"media/splatter.png", "media/witch_intro.png",
"media/item_description.png", "media/death_certificate.png",
"media/achievements.png", "media/boss_pictures.png",
"media/portrait_part.png", "media/dungeon_random.png",
"media/dungeon_objects.png", "media/shadows_standard.png",
"media/shadows_corners.png", "media/shadows_medium.png",
"media/shadows_small.png", "media/doors.png",
"media/destroyable_objects.png", "media/hall_of_fame.png",
"media/lightning.png",
"media/win_seal.png", "media/hof_win_seal.png",
"media/bag.png", "media/ui_pause.png",
- "media/score_font.png",
+ "media/score_font.png", "media/effect_zone.png",
};
for (const char *const filename : images)
ImageManager::getInstance().addImage(filename);
const char *const sounds[] =
{
"media/sound/blast00.ogg", "media/sound/blast01.ogg",
"media/sound/blast_elec.ogg", "media/sound/blast_fire.ogg",
"media/sound/blast_ice.ogg", "media/sound/blast_illusion.ogg",
"media/sound/blast_poison.ogg", "media/sound/blast_stone.ogg",
"media/sound/door_closing.ogg", "media/sound/door_opening.ogg",
"media/sound/chest_opening.ogg", "media/sound/impact.ogg",
"media/sound/bonus.ogg", "media/sound/drink.ogg",
"media/sound/eat.ogg", "media/sound/player_hit.ogg",
"media/sound/player_die.ogg", "media/sound/ennemy_dying.ogg",
"media/sound/coin.ogg", "media/sound/pay.ogg",
"media/sound/wall_impact.ogg", "media/sound/big_wall_impact.ogg",
"media/sound/king_rat_cry_1.ogg", "media/sound/king_rat_cry_2.ogg",
"media/sound/king_rat_die.ogg", "media/sound/slime_jump.ogg",
"media/sound/slime_impact.ogg", "media/sound/slime_impact_weak.ogg",
"media/sound/slime_impact_boss.ogg",
"media/sound/slime_smash.ogg", "media/sound/ice_charge.ogg",
"media/sound/electric.ogg", "media/sound/select.ogg",
"media/sound/heart.ogg", "media/sound/rat_die.ogg",
"media/sound/bat_die.ogg", "media/sound/imp_hurt.ogg",
"media/sound/imp_die.ogg", "media/sound/rock_impact.ogg",
"media/sound/rock_impact_medium.ogg", "media/sound/rock_impact_heavy.ogg",
"media/sound/throw.ogg", "media/sound/cyclop00.ogg",
"media/sound/cyclop_die.ogg", "media/sound/cyclops_impact.ogg",
"media/sound/butcher_00.ogg", "media/sound/butcher_01.ogg",
"media/sound/butcher_hurt.ogg", "media/sound/butcher_die.ogg",
"media/sound/vib.ogg",
"media/sound/boom_00.ogg", "media/sound/clang_00.ogg",
"media/sound/bubble_00.ogg", "media/sound/bubble_01.ogg",
"media/sound/trap.ogg", "media/sound/egg_smash_00.ogg",
"media/sound/egg_smash_01.ogg", "media/sound/spider_walking.ogg",
"media/sound/spider_web.ogg", "media/sound/spider_hurt.ogg",
"media/sound/spider_die.ogg", "media/sound/little_spider_die.ogg",
"media/sound/witch_00.ogg", "media/sound/witch_01.ogg",
"media/sound/witch_die_00.ogg", "media/sound/witch_die_01.ogg",
"media/sound/witch_02.ogg", "media/sound/invoke.ogg",
"media/sound/cauldron.ogg", "media/sound/cauldron_die.ogg",
"media/sound/snake_die.ogg", "media/sound/pumpkin_00.ogg",
"media/sound/pumpkin_01.ogg", "media/sound/pumpkin_die.ogg",
"media/sound/critical.ogg",
"media/sound/gong.ogg", "media/sound/teleport.ogg",
"media/sound/spell_charge.ogg", "media/sound/fireball.ogg",
"media/sound/message.ogg", "media/sound/earthquake.ogg",
"media/sound/spell_freeze.ogg", "media/sound/spell_shield.ogg",
"media/sound/heavy_step_00.ogg", "media/sound/heavy_step_01.ogg",
"media/sound/night.ogg", "media/sound/grumble.ogg",
"media/sound/zombie_00.ogg", "media/sound/zombie_01.ogg",
"media/sound/zombie_attack.ogg", "media/sound/zombie_die.ogg",
"media/sound/ghost.ogg", "media/sound/ghost_die.ogg",
"media/sound/electricity.ogg",
"media/sound/electric_blast.ogg", "media/sound/francky_00.ogg",
"media/sound/francky_01.ogg", "media/sound/francky_02.ogg",
"media/sound/francky_die.ogg", "media/sound/om.ogg",
"media/sound/glass.ogg", "media/sound/hiccup.ogg",
"media/sound/splatch.ogg", "media/sound/intro_witch.ogg",
"media/sound/force_field.ogg", "media/sound/door_opening_boss.ogg",
"media/sound/achievement.ogg",
"media/sound/vampire_flying.ogg", "media/sound/vampire_flap.ogg",
"media/sound/vampire_sonic.ogg", "media/sound/vampire_laughing.ogg",
"media/sound/vampire_transform_bolt.ogg", "media/sound/vampire_transform_bat.ogg",
"media/sound/vampire_hypnosis.ogg", "media/sound/vampire_cry.ogg",
"media/sound/vampire_dying.ogg", "media/sound/ice_block.ogg",
"media/sound/bogeyman_die.ogg", "media/sound/bogeyman_attack.ogg",
"media/sound/bogeyman_vortex_00.ogg", "media/sound/bogeyman_vortex_01.ogg",
"media/sound/barrel_hit.ogg", "media/sound/barrel_smash.ogg",
"media/sound/secret.ogg", "media/sound/scroll.ogg",
"media/sound/tic_tac.ogg", "media/sound/bottle.ogg",
"media/sound/thunder.ogg", "media/sound/bonus_score.ogg",
+ "media/sound/stone_hit.ogg",
};
// AA in fullscreen
if (parameters.fullscreen) enableAA(true);
// game main client position in the UI
xOffset = OFFSET_X;
yOffset = OFFSET_Y;
SoundManager::getInstance().setVolume(parameters.soundVolume);
for (const char *const filename : sounds)
{
SoundManager::getInstance().addSound(filename);
}
if (font.loadFromFile("media/DejaVuSans-Bold.ttf"))
{
myText.setFont(font);
}
graphicsFont.loadFromFile("media/Caudex-Bold.ttf");
miniMap = NULL;
currentMap = NULL;
currentFloor = NULL;
lastScore.score = 0;
lastScore.name = "";
lastScore.level = 0;
int i;
for (i = 0; i < NB_X_GAME; i++) xGame[i].active = false;
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++) equipNudeToDisplay[i] = false;
for (i = 0; i < NB_MESSAGES; i++) gameMessagesToSkip[i] = false;
for (i = 0; i < NB_ACHIEVEMENTS; i++) achievementState[i] = AchievementUndone;
for (i = 0; i < NB_ENEMY; i++) globalData.killedMonster[i] = 0;
saveInFight.monsters.clear();
showLogical = false;
showGameTime = false;
uiSprites.shotsSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_HUD_SHOTS));
uiSprites.topLayer.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_TOP_LAYER));
uiSprites.topLayer.setPosition(0, SCREEN_HEIGHT - 124);
uiSprites.msgBoxSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_MESSAGE));
uiSprites.mapBgSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_MAP_BACKGROUND));
uiSprites.mapBgSprite.setPosition(342, 23);
uiSprites.pauseSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_PAUSE));
uiSprites.pauseSprite.setPosition(SCREEN_WIDTH - 453, 0);
uiSprites.bagSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_BAG));
uiSprites.bagSprite.setPosition(116, 606);
uiSprites.numberSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_SCORE_FONT));
introScreenSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_INTRO));
titleSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_TITLE));
titleSprite.setOrigin(titleSprite.getTextureRect().width / 2, titleSprite.getTextureRect().height / 2);
scoreState = ScoreOK;
loopCounter = 0;
loadGameData();
loadHiScores();
receiveScoreFromServer();
srand(time(NULL));
fairySpriteOffsetY = 0;
if (isAdvanced()) fairySpriteOffsetY = 72 + 72 * (rand() % 4);
// init keys
for (int p = 0; p < NB_PLAYERS_MAX; p++)
{
for (unsigned int i = 0; i < NumberKeys; i++)
{
actionKey[p][i].isPressed = false;
actionKey[p][i].isTriggered = false;
}
}
nbPlayers = 1;
// click zones
for (int i = 0; i < 4; i++)
buttons.push_back(ButtonStruct { sf::IntRect(161 + 36 * i, 615, 32, 32), ButtonConsumable, i });
for (int i = 0; i < 5; i++)
buttons.push_back(ButtonStruct { sf::IntRect(124 + 55 * i, 655, 48, 48), ButtonShotType, i });
}
void WitchBlastGame::enableAA(bool enable)
{
for (int i = 0; i < NB_IMAGES; i++)
{
if (i != IMAGE_TILES && i != IMAGE_DUNGEON_OBJECTS
&& i != IMAGE_TILES_SHADOW && i != IMAGE_TILES_SHADOW_CORNER && i != IMAGE_TILES_SHADOW_MEDIUM
&& i != IMAGE_MINIMAP
&& i != IMAGE_DOORS
&& i != IMAGE_FOG
&& i != IMAGE_ITEMS_PRES && i != IMAGE_ITEMS_EQUIP_PRES && i != IMAGE_CORPSES
&& i != IMAGE_CORPSES_BIG)
ImageManager::getInstance().getImage(i)->setSmooth(enable);
}
}
WitchBlastGame::~WitchBlastGame()
{
// cleaning all entities
EntityManager::getInstance().clean();
// cleaning data
if (miniMap != NULL) delete (miniMap);
if (currentFloor != NULL) delete (currentFloor);
if (sendScoreThread.joinable()) sendScoreThread.join();
if (receiveScoreThread.joinable()) receiveScoreThread.join();
}
DungeonMap* WitchBlastGame::getCurrentMap()
{
return currentMap;
}
GameFloor* WitchBlastGame::getCurrentFloor()
{
return currentFloor;
}
int WitchBlastGame::getFloorX()
{
return floorX;
}
int WitchBlastGame::getFloorY()
{
return floorY;
}
DungeonMapEntity* WitchBlastGame::getCurrentMapEntity()
{
return dungeonEntity;
}
PlayerEntity* WitchBlastGame::getPlayer()
{
return player;
}
Vector2D WitchBlastGame::getPlayerPosition()
{
return Vector2D(player->getX(), player->getY());
}
int WitchBlastGame::getLevel()
{
return level;
}
int WitchBlastGame::getChallengeLevel()
{
return challengeLevel;
}
int WitchBlastGame::getSecretsFound()
{
return secretsFound;
}
bool WitchBlastGame::getShowLogical()
{
return showLogical;
}
float WitchBlastGame::getDeltaTime()
{
return deltaTime;
}
void WitchBlastGame::addHurtingStat(int hpLost)
{
statsData.hurtCounter ++;
statsData.hpLost += hpLost;
}
void WitchBlastGame::addHealingStat(int hpHeal)
{
statsData.healCounter ++;
statsData.hpHeal += hpHeal;
}
void WitchBlastGame::saveStats()
{
statsData.hpEnd = player->getHp();
statsData.hpMaxEnd = player->getHpMax();
statsData.dam = player->getDamage();
std::ostringstream oss;
oss << "{";
oss << int(gameTime);
oss << "(" << statsData.hpIni << "/" << statsData.hpMax << "->(" << statsData.hpEnd << "/" << statsData.hpMaxEnd << ")[" << statsData.dam << "]";
oss << "(" << statsData.hurtCounter << "x[" << statsData.hpLost << "])";
oss << "(" << statsData.healCounter << "x[" << statsData.hpHeal << "])";
oss << "$" << player->getGold();
oss << "}";
statsStr += oss.str();
}
float WitchBlastGame::getGameTime()
{
return gameTime;
}
void WitchBlastGame::onUpdate()
{
if (gameState == gameStatePlaying)
{
if (isPlayerAlive) player->setItemToBuy(NULL);
loopCounter++;
if (loopCounter > 3) loopCounter = 0;
// time stop
if (player->isSpecialStateActive(SpecialStateTime))
{
if (loopCounter == 0)
EntityManager::getInstance().animate(deltaTime);
else
player->animate(deltaTime);
SoundManager::getInstance().playSound(SOUND_CLOCK, false);
}
else
{
EntityManager::getInstance().animate(deltaTime);
if (isPressing(0, KeyTimeControl, false))
{
EntityManager::getInstance().animate(deltaTime);
SoundManager::getInstance().playSound(SOUND_VIB, false);
}
}
if (isPlayerAlive) gameTime += deltaTime;
for (int i = 0; i < NB_X_GAME; i++)
{
if (xGame[i].active)
{
xGame[i].timer -= deltaTime;
if (xGame[i].timer <= 0.0f)
{
xGame[i].active = false;
if (i == (int)xGameTypeFade && xGame[i].param == X_GAME_FADE_OUT)
{
if (player->getPlayerStatus() == PlayerEntity::playerStatusGoingNext)
{
level++;
saveStats();
startNewLevel();
}
else
startNewGame(false, 1);
}
}
}
}
if (isPlayerAlive)
{
if (player->getHp() <= 0)
{
isPlayerAlive = false;
playMusic(MusicEnding);
}
}
}
else if (gameState == gameStatePlayingDisplayBoss)
{
bossDisplayTimer += deltaTime;
if (bossDisplayTimer >= PORTRAIT_DIAPLAY_TIME) gameState = gameStatePlaying;
}
}
void WitchBlastGame::startNewGame(bool fromSaveFile, int startingLevel)
{
gameState = gameStateInit;
level = 1;
score = 0;
bodyCount = 0;
scoreDisplayed = 0;
scoreBonus = "";
scoreBonusTimer = -1.0f;
challengeLevel = 1;
secretsFound = 0;
gameTime = 0.0f;
initEvents();
scoreSaveFile = "";
interaction.active = false;
endingTimer = -1.0f;
statsStr = "";
// cleaning all entities
EntityManager::getInstance().clean();
SoundManager::getInstance().stopSound(SOUND_NIGHT);
// cleaning the message queue
std::queue<messageStruct> empty;
std::swap( messagesQueue, empty );
// cleaning the achievements queue
std::queue<achievementStruct> empty2;
std::swap( achievementsQueue, empty2 );
// cleaning data
if (miniMap != NULL) delete (miniMap);
if (currentFloor != NULL) delete (currentFloor);
miniMap = NULL;
currentFloor = NULL;
// init in game menu
buildInGameMenu();
dungeonEntity = new DungeonMapEntity();
// the interface
uiSprites.gui.setTexture(*ImageManager::getInstance().getImage(IMAGE_INTERFACE));
// key symbol on the interface
uiSprites.keySprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
uiSprites.keySprite.setTextureRect(sf::IntRect(ITEM_WIDTH * EQUIP_BOSS_KEY, 0, ITEM_WIDTH, ITEM_HEIGHT));
uiSprites.keySprite.setPosition(582, 612);
miniMap = new GameMap(FLOOR_WIDTH, FLOOR_HEIGHT);
// minimap on the interface
miniMapEntity = new MiniMapEntity(ImageManager::getInstance().getImage(IMAGE_MINIMAP), miniMap, 19, 15, 10);
miniMapEntity->setTileBox(20, 16);
miniMapEntity->setX(705);
miniMapEntity->setY(610);
miniMapEntity->setZ(10001.0f);
// doors
doorEntity[0] = new DoorEntity(8);
doorEntity[1] = new DoorEntity(4);
doorEntity[2] = new DoorEntity(2);
doorEntity[3] = new DoorEntity(6);
saveInFight.isFight = false;
if (fromSaveFile)
{
if (!loadGame())
{
fromSaveFile = false;
}
else
{
gameFromSaveFile = true;
playLevel(saveInFight.isFight);
}
}
if (!fromSaveFile)
{
gameFromSaveFile = false;
// the player
player = new PlayerEntity((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f));
// Add a fairy - co op players
if (nbPlayers == 2)
{
int r = rand() % 10;
int hp = 12;
int fairy = ItemFairy - FirstEquipItem;
switch (r)
{
case 0: fairy = ItemFairyIce - FirstEquipItem; break;
case 1: fairy = ItemFairyStone - FirstEquipItem; break;
case 2: fairy = ItemFairyPoison - FirstEquipItem; break;
case 3: fairy = ItemFairyFire - FirstEquipItem; break;
}
player->setEquipped(fairy, true, true);
player->setHpMax(hp);
player->setHp(hp);
}
resetKilledEnemies();
randomizePotionMap();
if (startingLevel > 1)
{
player->acquireItem(ItemScrollRevelation);
player->acquireItem(enumItemType(ItemPotion01 + rand() % NUMBER_UNIDENTIFIED));
for (int i = 1; i < startingLevel; i++)
{
level = i;
if (level == 3)
{
player->acquireItem(getItemSpell());
player->acquireItemAfterStance();
}
player->setHpMax(player->getHpMax() + 2 + rand() % 4);
item_equip_enum item = getRandomEquipItem(false, false);
player->acquireItem((enumItemType)(item + FirstEquipItem));
player->acquireItemAfterStance();
item = getRandomEquipItem(true, true);
player->acquireItem((enumItemType)(item + FirstEquipItem));
player->acquireItemAfterStance();
if (i % 2 == 0)
{
item = getRandomEquipItem(false, false);
player->acquireItem((enumItemType)(item + FirstEquipItem));
player->acquireItemAfterStance();
}
}
level++;
player->setHp(player->getHpMax());
player->setGold(8 + rand() % 45);
if (startingLevel > 2)
{
player->loadDivinity(rand() % NB_DIVINITY, (startingLevel - 2) * 200, 1, 0);
}
}
startNewLevel();
}
}
void WitchBlastGame::startNewLevel()
{
// reset floor items
player->resetFloorItem();
// reset present items
resetPresentItems();
bool needShop = false;
// create the new level
if (currentFloor != NULL)
{
if (level > 1)
{
if (!currentFloor->hasRoomOfType(roomTypeMerchant)) needShop = true;
}
delete currentFloor;
}
currentFloor = new GameFloor(level);
if (needShop) currentFloor->setForceShop();
currentFloor->createFloor();
// center it
floorX = FLOOR_WIDTH / 2;
floorY = FLOOR_HEIGHT / 2;
// move the player
if (level == 1)
player->moveTo((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f));
else
player->moveTo((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT - 2 * TILE_HEIGHT));
// the boss room is closed
bossRoomOpened = false;
if (level <= 7) testAndAddMessageToQueue((EnumMessages)(MsgInfoLevel1 + level - 1));
if (level == 1)
{
testAndAddMessageToQueue(MsgTutoBasics);
testAndAddMessageToQueue(MsgTutoTips);
}
playLevel(false);
}
void WitchBlastGame::playLevel(bool isFight)
{
isPlayerAlive = true;
if (!isFight)
{
player->setVelocity(Vector2D(0.0f, 0.0f));
player->setPlayerStatus(PlayerEntity::playerStatusPlaying);
}
// first map is open
roomClosed = false;
// generate the map
refreshMap();
// items from save
currentMap->restoreMapObjects();
if (isFight)
{
checkEntering();
saveMapItems();
}
// populate with monsters
if (isFight)
{
auto monsters_save = saveInFight.monsters;
saveInFight.monsters.clear();
for (auto monster: monsters_save)
addMonster(monster.id, monster.x, monster.y);
if (currentMap->getRoomType() == roomTypeBoss) playMusic(MusicBoss);
else if (currentMap->getRoomType() == roomTypeChallenge) playMusic(MusicChallenge);
else playMusic(MusicDungeon);
}
else
playMusic(MusicDungeon);
// game time counter an state
lastTime = getAbsolutTime();
gameState = gameStatePlaying;
// fade in
xGame[xGameTypeFade].active = true;
xGame[xGameTypeFade].param = X_GAME_FADE_IN;
xGame[xGameTypeFade].timer = FADE_IN_DELAY;
float x0 = MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = MAP_HEIGHT * 0.5f * TILE_HEIGHT + 40.0f;
std::ostringstream oss;
oss << tools::getLabel("level") << " " << level;
TextEntity* text = new TextEntity(oss.str(), 30, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1000);
text->setColor(TextEntity::COLOR_FADING_WHITE);
statsData.goldIni = player->getGold();
statsData.hpIni = player->getHp();
statsData.hpMax = player->getHpMax();
statsData.hpLost = 0;
statsData.hpHeal = 0;
statsData.hurtCounter = 0;
statsData.healCounter = 0;
+
+ if (autosave) saveGame(true);
}
void WitchBlastGame::prepareIntro()
{
EntityManager::getInstance().clean();
introSprites[0] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_FOG));
introSprites[0]->setX(SCREEN_WIDTH / 2);
introSprites[0]->setY(SCREEN_HEIGHT - 202);
introSprites[1] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_FOG));
introSprites[1]->setX(SCREEN_WIDTH / 2 + 970);
introSprites[1]->setY(SCREEN_HEIGHT - 202);
gameState = gameStateIntro;
gameTime = 0.0f;
introState = 0;
introSoundState = 0;
SoundManager::getInstance().playSound(SOUND_NIGHT);
}
void WitchBlastGame::updateIntro()
{
gameTime += deltaTime;
bool toMenu = false;
for (int i = 0; i < 2; i++)
{
introSprites[i]->setX(introSprites[i]->getX() - deltaTime * 35);
if (introSprites[i]->getX() < - SCREEN_WIDTH / 2) introSprites[i]->setX(introSprites[i]->getX() + 2 * SCREEN_WIDTH);
}
if (gameTime > 2.0f && introSoundState == 0)
{
SoundManager::getInstance().playSound(SOUND_INTRO_WITCH);
introSoundState++;
}
else if (gameTime > 7.0f && introSoundState == 1)
{
playMusic(MusicIntro);
introSoundState++;
}
if (introState == 0 && gameTime > 2.5f)
{
introState = 1;
introSprites[2] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_TITLE_ANIM),
SCREEN_WIDTH / 2,
SCREEN_HEIGHT / 2,
310, 278, 3);
introSprites[2]->setZ(50);
}
else if (introState == 1)
{
int frame = (gameTime - 2.5f) * 8.0f;
if (frame > 16)
{
frame = 16;
introState = 2;
introSprites[3] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_WITCH_INTRO));
introSprites[3]->setX(368 + 1000);
introSprites[3]->setY(424 - 480);
introSprites[3]->setZ(40);
EntityManager::getInstance().sortByZ();
}
introSprites[2]->setFrame(frame);
}
else if (introState == 2)
{
float xSprite = introSprites[3]->getX() - deltaTime * 800;
float ySprite = introSprites[3]->getY() + deltaTime * 400;
if (xSprite < 368)
{
introState = 3;
introSprites[3]->setDying(true);
EntityManager::getInstance().animate(deltaTime);
xSprite = 368;
ySprite = 424;
introSprites[4] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_SPLATTER));
introSprites[4]->setX(368);
introSprites[4]->setY(424);
introSprites[4]->setZ(45);
introSprites[4]->setScale(0.5f, 0.5f);
EntityManager::getInstance().sortByZ();
SoundManager::getInstance().stopSound(SOUND_INTRO_WITCH);
SoundManager::getInstance().playSound(SOUND_SPLATCH);
}
else
{
introSprites[3]->setX(xSprite);
introSprites[3]->setY(ySprite);
introSprites[3]->setAngle(gameTime * 500);
}
}
else if (introState == 3)
{
float scale = introSprites[4]->getScaleX();
scale += deltaTime * 5.0f;
if (scale > 1.0f) scale = 1.0f;
introSprites[4]->setScale(scale, scale);
if (gameTime > 8.0f)
{
introState = 4;
introSprites[2]->setDying(true);
introSprites[4]->setDying(true);
EntityManager::getInstance().animate(deltaTime);
toMenu = true;
}
}
sf::Event event;
bool stopDemo = false;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
//saveGameData();
app->close();
}
if (event.type == sf::Event::Resized)
{
enableAA(true);
sf::View view = app->getDefaultView();
view = getLetterboxView( view, event.size.width, event.size.height );
app->setView(view);
}
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape) stopDemo = true;
}
}
if (isPressing(0, KeyFireDown, true)) stopDemo = true;
if (stopDemo)
{
if (introState < 4)
{
if (introState > 0) introSprites[2]->setDying(true);
if (introState == 2) introSprites[3]->setDying(true);
if (introState > 2) introSprites[4]->setDying(true);
EntityManager::getInstance().animate(deltaTime);
if (introState < 3)
{
SoundManager::getInstance().stopSound(SOUND_INTRO_WITCH);
SoundManager::getInstance().playSound(SOUND_SPLATCH);
}
}
toMenu = true;
}
if (toMenu)
{
if (introSoundState <= 1) playMusic(MusicIntro);
switchToMenu();
}
}
void WitchBlastGame::renderIntro()
{
app->draw(introScreenSprite);
titleSprite.setPosition(SCREEN_WIDTH / 2 - 15, 371);
if (introState == 4) app->draw(titleSprite);
EntityManager::getInstance().render(app);
}
void WitchBlastGame::updateRunningGame()
{
bool backToMenu = false;
bool escape = false;
// Process events
sf::Event event;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
- if ((gameState == gameStatePlaying && !player->isDead()) || gameState == gameStatePlayingPause) saveGame();
+ if ((gameState == gameStatePlaying && !player->isDead()) || gameState == gameStatePlayingPause) saveGame(false);
saveGameData();
app->close();
}
if (event.type == sf::Event::Resized)
{
enableAA(true);
sf::View view = app->getDefaultView();
view = getLetterboxView( view, event.size.width, event.size.height );
app->setView(view);
}
if (event.type == sf::Event::MouseWheelMoved)
{
if (gameState == gameStatePlaying) player->selectNextShotType();
}
if (event.type == sf::Event::MouseButtonPressed)
{
if (gameState == gameStatePlaying)
{
sf::Vector2i mousePositionInWindow = sf::Mouse::getPosition(*app);
sf::Vector2f mousePosition = app->mapPixelToCoords(mousePositionInWindow);
int mouseButton = 1;
if (event.mouseButton.button == sf::Mouse::Left) mouseButton = 0;
tryToClick(mousePosition.x, mousePosition.y, mouseButton);
}
}
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape)
{
escape = true;
}
if (event.key.code == sf::Keyboard::Return && gameState == gameStatePlaying)
{
if (!achievementsQueue.empty())
{
if (achievementsQueue.front().timer > 1.0f)
achievementsQueue.front().timer = 1.0f;
}
else if (!messagesQueue.empty())
{
if (messagesQueue.front().timer > 0.5f)
messagesQueue.front().timer = 0.5f;
}
}
if (event.key.code == sf::Keyboard::X)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) startNewGame(false, 1);
}
if (event.key.code >= sf::Keyboard::Num1 && event.key.code <= sf::Keyboard::Num8)
{
#ifdef LEVEL_TEST_MODE
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt) && sf::Keyboard::isKeyPressed(sf::Keyboard::LControl))
{
startNewGame(false, event.key.code - sf::Keyboard::Num1 + 1);
}
else
#endif
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)
|| sf::Keyboard::isKeyPressed(sf::Keyboard::RShift))
{
player->dropConsumables(event.key.code - sf::Keyboard::Num1);
}
else
{
player->tryToConsume(event.key.code - sf::Keyboard::Num1);
}
}
if (event.key.code == sf::Keyboard::F1)
{
if (!isPlayerAlive && player->getEndAge() > 3.5f)
{
if (scoreSaveFile.compare("") == 0) saveDeathScreen();
}
else
{
saveScreen();
}
}
if (event.key.code == sf::Keyboard::F2)
{
showLogical = !showLogical;
}
if (event.key.code == sf::Keyboard::F3)
{
showGameTime = !showGameTime;
}
// DEBUG
#ifdef TEST_MODE
/*if (event.key.code == sf::Keyboard::Delete)
{
StructHurt hurt;
hurt.critical = false;
hurt.damage = 1;
hurt.enemyType = EnemyTypeGhost; //EnemyTypeNone;
hurt.goThrough = false;
hurt.hurtingType = ShotTypeStandard;
hurt.level = 0;
hurt.sourceType = SourceTypeMelee;
player->hurt(hurt);
}*/
if (event.key.code == sf::Keyboard::F5)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new BubbleEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 + 70,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2, BubbleTriple, 0);
new BubbleEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 - 70,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2, BubbleIce, 0);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeRat, 1);
findPlaceMonsters(EnemyTypeRatBlack, 1);
findPlaceMonsters(EnemyTypeRatGreen, 1);
findPlaceMonsters(EnemyTypeRatHelmet, 1);
findPlaceMonsters(EnemyTypeRatBlackHelmet, 1);
}
}
if (event.key.code == sf::Keyboard::F6)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new ButcherEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeBat, 1);
findPlaceMonsters(EnemyTypeBatSkeleton, 2);
findPlaceMonsters(EnemyTypeImpBlue, 1);
findPlaceMonsters(EnemyTypeImpRed, 1);
}
}
if (event.key.code == sf::Keyboard::F7)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new GiantSlimeEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeEvilFlower, 1);
findPlaceMonsters(EnemyTypeEvilFlowerIce, 1);
findPlaceMonsters(EnemyTypeEvilFlowerFire, 1);
findPlaceMonsters(EnemyTypePumpkin, 1);
}
}
if (event.key.code == sf::Keyboard::F8)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new CyclopsEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeSlime, 1);
findPlaceMonsters(EnemyTypeSlimeBlue, 1);
findPlaceMonsters(EnemyTypeSlimeRed, 1);
findPlaceMonsters(EnemyTypeSlimeViolet, 1);
}
}
if (event.key.code == sf::Keyboard::F9)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new KingRatEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeSnake, 2);
findPlaceMonsters(EnemyTypeSnakeBlood, 2);
}
}
if (event.key.code == sf::Keyboard::F10)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new GiantSpiderEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeWitch, 1);
findPlaceMonsters(EnemyTypeWitchRed, 1);
findPlaceMonsters(EnemyTypeCauldron, 1);
findPlaceMonsters(EnemyTypeCauldronElemental, 1);
}
}
if (event.key.code == sf::Keyboard::F11)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new FranckyEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeSpiderEgg_invocated, 2);
findPlaceMonsters(EnemyTypeSpiderLittle, 2);
findPlaceMonsters(EnemyTypeSpiderTarantula, 2);
}
}
if (event.key.code == sf::Keyboard::F12)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt))
{
new VampireEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
initMonsterArray();
findPlaceMonsters(EnemyTypeZombie, 2);
findPlaceMonsters(EnemyTypeZombieDark, 2);
findPlaceMonsters(EnemyTypeGhost, 2);
findPlaceMonsters(EnemyTypeBogeyman, 2);
}
}
if (event.key.code == sf::Keyboard::F4)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift))
for (int i = 0; i < NUMBER_ITEMS - NUMBER_EQUIP_ITEMS; i++)
new ItemEntity((enumItemType)i, 100 + (i % 14) * 58, 100 + (i / 14) * 60);
else
for (int i = NUMBER_ITEMS - NUMBER_EQUIP_ITEMS; i < NUMBER_ITEMS; i++)
new ItemEntity((enumItemType)i, 100 + ( (i - NUMBER_ITEMS + NUMBER_EQUIP_ITEMS) % 14) * 58, 100 + ((i - NUMBER_ITEMS + NUMBER_EQUIP_ITEMS) / 14) * 60);
}
#endif // TEST_MODE
}
}
// POST EVENT
if (escape)
{
if (player->isDead() || player->getPlayerStatus() == PlayerEntity::playerStatusVictorious) backToMenu = true;
else if (gameState == gameStatePlaying) gameState = gameStatePlayingPause;
else if (gameState == gameStatePlayingPause) gameState = gameStatePlaying;
else if (gameState == gameStatePlayingDisplayBoss) gameState = gameStatePlaying;
}
if ( (player->isDead() || player->getPlayerStatus() == PlayerEntity::playerStatusVictorious) && !xGame[xGameTypeFade].active && isPressing(0, KeyFireDown, true))
{
if (player->getEndAge() < DEATH_CERTIFICATE_DELAY) player->setEndAge(DEATH_CERTIFICATE_DELAY);
else backToMenu = true;
}
else if (gameState == gameStatePlayingPause)
{
if (isPressing(0, KeyDown, true))
{
menuInGame.index++;
if (menuInGame.index == menuInGame.items.size()) menuInGame.index = 0;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (isPressing(0, KeyUp, true))
{
if (menuInGame.index == 0) menuInGame.index = menuInGame.items.size() - 1;
else menuInGame.index--;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (isPressing(0, KeyFireDown, true))
{
switch (menuInGame.items[menuInGame.index].id)
{
case MenuStartNew:
case MenuStartOld:
case MenuKeys:
case MenuJoystick:
case MenuConfig:
case MenuTutoReset:
case MenuConfigBack:
case MenuLanguage:
case MenuCredits:
case MenuHiScores:
case MenuPlayerName:
case MenuVolumeMusic:
case MenuVolumeSound:
case MenuAchievements:
std::cout << "[ERROR] Bad Menu ID\n";
break;
case MenuExit:
backToMenu = true;
remove(SAVE_FILE.c_str());
break;
case MenuContinue:
gameState = gameStatePlaying;
break;
case MenuSaveAndQuit:
- if (currentMap->isCleared()) saveGame();
+ if (currentMap->isCleared()) saveGame(false);
backToMenu = true;
break;
}
}
}
else if (gameState == gameStatePlaying)
{
if (player->canMove()) player->setVelocity(Vector2D(0.0f, 0.0f));
if (isPressing(0, KeyLeft, false))
{
if (isPressing(0, KeyUp, false))
player->move(7);
else if (isPressing(0, KeyDown, false))
player->move(1);
else
player->move(4);
}
else if (isPressing(0, KeyRight, false))
{
if (isPressing(0, KeyUp, false))
player->move(9);
else if (isPressing(0, KeyDown, false))
player->move(3);
else
player->move(6);
}
else if (isPressing(0, KeyUp, false))
{
player->move(8);
}
else if (isPressing(0, KeyDown, false))
{
player->move(2);
}
if (isPressing(0, KeyInteract, true))
{
if (!player->isDead() && interaction.active) player->interact(interaction.type, interaction.id);
}
if (isPressing(0, KeyFireSelect, true))
{
if (gameState == gameStatePlaying) player->selectNextShotType();
}
if (isPressing(0, KeySpell, true))
{
if (gameState == gameStatePlaying) player->castSpell();
}
player->resetFireDirection();
if (isPressing(0, KeyFireLeft, false))
player->fire(4);
else if (isPressing(0, KeyFireRight, false))
player->fire(6);
else if (isPressing(0, KeyFireUp, false))
player->fire(8);
else if (isPressing(0, KeyFireDown, false))
player->fire(2);
// alternative "one button" gameplay
else if (isPressing(0, KeyFire, false))
{
if (gameState == gameStatePlaying && isPressing(0, KeyFire, true))
firingDirection = player->getFacingDirection();
player->fire(firingDirection);
}
// alternative "firing with the mouse" gameplay
else if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::Vector2i mousePositionInWindow = sf::Mouse::getPosition(*app);
sf::Vector2f mousePosition = app->mapPixelToCoords(mousePositionInWindow);
int xm = mousePosition.x - player->getX();
int ym = mousePosition.y - player->getY();
if (mousePosition.x >= xOffset && mousePosition.x <= xOffset + GAME_WIDTH
&& mousePosition.y >= yOffset && mousePosition.y <= yOffset + GAME_HEIGHT)
{
if (abs(xm) >= abs(ym))
{
if (xm > 0) player->fire(6);
else player->fire(4);
}
else
{
if (ym > 0) player->fire(2);
else player->fire(8);
}
}
}
// spell (right click)
if (sf::Mouse::isButtonPressed(sf::Mouse::Right) && (gameState == gameStatePlaying))
{
sf::Vector2i mousePositionInWindow = sf::Mouse::getPosition(*app);
sf::Vector2f mousePosition = app->mapPixelToCoords(mousePositionInWindow);
if (mousePosition.x >= xOffset && mousePosition.x <= xOffset + GAME_WIDTH
&& mousePosition.y >= yOffset && mousePosition.y <= yOffset + GAME_HEIGHT)
{
int xm = mousePosition.x - player->getX();
int ym = mousePosition.y - player->getY();
if (abs(xm) >= abs(ym))
{
if (xm > 0) player->setFacingDirection(6);
else player->setFacingDirection(4);
}
else
{
if (ym > 0) player->setFacingDirection(2);
else player->setFacingDirection(8);
}
player->castSpell();
}
}
// Joystick control for fairy
if (nbPlayers == 2)
{
auto fairy = player->getFairy(0);
if (fairy)
{
fairy->setVelocity(Vector2D{0.0f, 0.0f});
if (isPressing(1, KeyLeft, false))
{
if (isPressing(1, KeyUp, false))
fairy->move(7);
else if (isPressing(1, KeyDown, false))
fairy->move(1);
else
fairy->move(4);
}
else if (isPressing(1, KeyRight, false))
{
if (isPressing(1, KeyUp, false))
fairy->move(9);
else if (isPressing(1, KeyDown, false))
fairy->move(3);
else
fairy->move(6);
}
else if (isPressing(1, KeyUp, false))
{
fairy->move(8);
}
else if (isPressing(1, KeyDown, false))
{
fairy->move(2);
}
if (isPressing(1, KeyFireLeft, false))
fairy->fire(4, true);
else if (isPressing(1, KeyFireRight, false))
fairy->fire(6, true);
else if (isPressing(1, KeyFireUp, false))
fairy->fire(8, true);
else if (isPressing(1, KeyFireDown, false))
fairy->fire(2, true);
}
}
// message queue
if (!messagesQueue.empty())
{
messagesQueue.front().timer -= deltaTime;
if (messagesQueue.front().timer < 0.0f)
{
messagesQueue.pop();
if (!messagesQueue.empty()) SoundManager::getInstance().playSound(SOUND_MESSAGE);
}
}
// achievement queue
if (!achievementsQueue.empty() && achievementsQueue.front().hasStarted)
{
achievementsQueue.front().timer -= deltaTime;
if (achievementsQueue.front().timer < 0.0f)
{
achievementsQueue.pop();
}
}
// victorious end of game ?
if (endingTimer > 0.0f)
{
endingTimer -= deltaTime;
if (endingTimer <= 0.0f)
{
calculateScore();
player->setPlayerStatus(PlayerEntity::playerStatusVictorious);
player->setVelocity(Vector2D(0, 0));
}
else if (endingTimer <= 5.5f && !isBonusTimeAdded)
{
isBonusTimeAdded = true;
int timeScore = getTimeScore((int)(gameTime / 60.0f));
addBonusScore(BonusTime, timeScore);
}
}
}
else if (gameState == gameStatePlayingDisplayBoss)
{
if (isPressing(0, KeyFireDown, true)) gameState = gameStatePlaying;
}
onUpdate();
verifyDoorUnlocking();
checkInteraction();
if (roomClosed)
{
if (getEnemyCount() == 0)
{
currentMap->setCleared(true);
if (currentMap->getRoomType() == roomTypeKey)
new ItemEntity( (enumItemType)(ItemBossKey),
MAP_WIDTH / 2 * TILE_WIDTH + TILE_WIDTH / 2,
MAP_HEIGHT / 2 * TILE_HEIGHT + TILE_HEIGHT / 2);
player->onClearRoom();
openDoors();
- remove(SAVE_FILE.c_str());
+ if (!autosave) remove(SAVE_FILE.c_str());
if (currentMap->getRoomType() == roomTypeBoss)
{
playMusic(MusicDungeon);
}
else if (currentMap->getRoomType() == roomTypeChallenge && !player->isDead())
{
ChestEntity* chest = new ChestEntity((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f),
ChestChallenge, false);
chest->makeAppear();
// sound
SoundManager::getInstance().playSound(SOUND_GONG);
playMusic(MusicDungeon);
// text
float x0 = MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = MAP_HEIGHT * 0.5f * TILE_HEIGHT + 40.0f;
TextEntity* text = new TextEntity("COMPLETE !", 30, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1200);
text->setColor(TextEntity::COLOR_FADING_WHITE);
addBonusScore(BonusChallenge, getChallengeScore(challengeLevel));
challengeLevel++;
if (challengeLevel == 5) registerAchievement(AchievementChallenges);
player->offerChallenge();
}
}
}
if (backToMenu)
{
if (nbPlayers == 1 && (player->isDead() || player->getPlayerStatus() == PlayerEntity::playerStatusVictorious))
{
EntityManager::getInstance().clean();
introSprites[0] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_FOG));
introSprites[0]->setX(SCREEN_WIDTH / 2);
introSprites[0]->setY(SCREEN_HEIGHT - 202);
introSprites[1] = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_FOG));
introSprites[1]->setX(SCREEN_WIDTH / 2 + 970);
introSprites[1]->setY(SCREEN_HEIGHT - 202);
switchToMenu();
updateActionKeys();
menuState = MenuStateHiScores;
#ifdef ONLINE_MODE
menuScoreIndex = 0;
#else
menuScoreIndex = 2;
#endif
}
else
{
prepareIntro();
switchToMenu();
menuState = MenuStateMain;
}
}
}
void WitchBlastGame::addLifeBarToDisplay(std::string label, int hp, int hpMax)
{
lifeBar.toDisplay = true;
lifeBar.label = label;
lifeBar.hp = hp;
lifeBar.hpMax = hpMax;
}
void WitchBlastGame::renderBossPortrait()
{
if (gameState == gameStatePlayingDisplayBoss)
{
float transitionTime = 2.5f;
float endTime = PORTRAIT_DIAPLAY_TIME;
int xBoss = GAME_WIDTH / 2 - 267;
int fade = 255;
if (bossDisplayTimer < 0.25f)
{
xBoss += (0.25f - bossDisplayTimer) * 3000;
//fade = 255 - (0.25f - bossDisplayTimer) * 1000;
}
else if (bossDisplayTimer > endTime - 0.75f)
{
fade = (endTime - bossDisplayTimer) * 333;
if (fade < 0) fade = 0;
}
// background
sf::RectangleShape rectangle;
rectangle.setFillColor(sf::Color(0, 0, 0, fade));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH, MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
// boss
if (bossDisplayTimer > transitionTime) // + 1.0f)
{
EnemyEntity* boss = getBoss();
if (boss)
{
sf::View view = app->getView();
view.move(-5, -5);
app->setView(view);
boss->render(app);
view.move(5, 5);
app->setView(view);
}
/*if (bossDisplayTimer < transitionTime + 1.5f)
{
rectangle.setFillColor(sf::Color(0, 0, 0, (1.5 - (bossDisplayTimer - transitionTime)) * 510));
app->draw(rectangle);
std::cout << 1.5 - (bossDisplayTimer - transitionTime) << " ";
}*/
//if (bossDisplayTimer < transitionTime + 2.0f)
{
//rectangle.setFillColor(sf::Color(0, 0, 0, (2.0 - (bossDisplayTimer - transitionTime)) * 255));
//app->draw(rectangle);
//std::cout << 1.5 - (bossDisplayTimer - transitionTime) << " ";
}
}
// boss portrait
if (bossDisplayTimer < transitionTime)
{
sf::Sprite bossSprite;
bossSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_BOSS_PICTURES));
bossSprite.setPosition(xBoss, 0);
app->draw(bossSprite);
}
else
{
if (bossDisplayState == 0)
{
SoundManager::getInstance().playSound(SOUND_BOOM_00);
for (int i = 0; i < 250; i++)
{
SpriteEntity* spriteStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_PORTRAIT_PART),
GAME_WIDTH / 2 - 150 + rand() % 300, GAME_HEIGHT / 2 - 150 + rand() % 300);
spriteStar->setScale(5.0f, 5.0f);
spriteStar->setZ(7000.0f);
spriteStar->setSpin(-100 + rand()%200);
spriteStar->setVelocity(Vector2D(400 + rand()%800));
spriteStar->setFading(true);
spriteStar->setLifetime(4.1f + (rand() % 100) * 0.003f );
spriteStar->setColor(sf::Color(rand() % 255, rand() % 255, 255, 128));
spriteStar->setColor(sf::Color(40, 4, 40, 128));
spriteStar->setType(ENTITY_EFFECT);
if (rand()% 2 == 0) spriteStar->setRenderAdd();
}
bossDisplayState = 1;
}
else if (bossDisplayState == 1 && bossDisplayTimer > transitionTime + 0.2f)
{
bossDisplayState = 2;
for (int i = 0; i < 50; i++)
{
SpriteEntity* spriteStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_PORTRAIT_PART),
GAME_WIDTH / 2 - 150 + rand() % 300, GAME_HEIGHT / 2 - 150 + rand() % 300);
spriteStar->setScale(4.0f, 4.0f);
spriteStar->setZ(7000.0f);
spriteStar->setSpin(-100 + rand()%200);
spriteStar->setVelocity(Vector2D(400 + rand()%800));
spriteStar->setFading(true);
spriteStar->setLifetime(4.1f + (rand() % 100) * 0.003f );
spriteStar->setColor(sf::Color(rand() % 255, rand() % 255, 255, 128));
spriteStar->setColor(sf::Color(20, 2, 20, 128));
spriteStar->setType(ENTITY_EFFECT);
spriteStar->setRenderAdd();
}
}
else if (bossDisplayState == 2 && bossDisplayTimer > transitionTime + 0.4f)
{
bossDisplayState = 3;
for (int i = 0; i < 50; i++)
{
SpriteEntity* spriteStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_PORTRAIT_PART),
GAME_WIDTH / 2 - 150 + rand() % 300, GAME_HEIGHT / 2 - 150 + rand() % 300);
spriteStar->setScale(3.0f, 3.0f);
spriteStar->setZ(7000.0f);
spriteStar->setSpin(-100 + rand()%200);
spriteStar->setVelocity(Vector2D(400 + rand()%800));
spriteStar->setFading(true);
spriteStar->setLifetime(4.1f + (rand() % 100) * 0.003f );
spriteStar->setColor(sf::Color(rand() % 255, rand() % 255, 255, 128));
spriteStar->setColor(sf::Color(20, 2, 20, 128));
spriteStar->setType(ENTITY_EFFECT);
spriteStar->setRenderAdd();
}
}
animateEffects();
}
}
}
void WitchBlastGame::renderGame()
{
lifeBar.toDisplay = false;
EntityManager::getInstance().renderUnder(app, 5000);
EntityManager::getInstance().renderUnder(app, 5000);
}
void WitchBlastGame::generateUiParticle(float x, float y)
{
SpriteEntity* particle = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_BOLT),
x, y, BOLT_WIDTH, BOLT_HEIGHT);
particle->setFading(true);
particle->setImagesProLine(BOLT_PRO_LINE);
particle->setZ(10000);
particle->setLifetime(0.5f);
particle->setVelocity(Vector2D(30 + rand() % 50));
particle->setWeight(200);
particle->setType(ENTITY_EFFECT);
//particle->setFrame(BOLT_PRO_LINE + 2);
particle->setFrame(BOLT_PRO_LINE * 2 + 6);
particle->setRenderAdd();
particle->setScale(0.35f, 0.35f);
particle->setFading(true);
}
void WitchBlastGame::renderHud()
{
// boss life bar ?
if (lifeBar.toDisplay && gameState != gameStatePlayingDisplayBoss) renderLifeBar();
//renderBossPortrait();
// interaction text ?
if (interaction.active)
{
if (interaction.type == InteractionTypeTemple)
write(interaction.label, 20, GAME_WIDTH / 2, 480, ALIGN_CENTER,sf::Color::White, app, 2, 2, 0);
else if (interaction.type == InteractionTypeMerchandise)
write(interaction.label, 20, GAME_WIDTH / 2, 480, ALIGN_CENTER,sf::Color::White, app, 2, 2, 0);
}
// light cone ?
float fade = player->getFadingDivinity(true);
if (fade > 0.0f)
{
sf::Sprite cone;
cone.setTexture(*ImageManager::getInstance().getImage(IMAGE_LIGHT_CONE));
cone.setPosition(player->getX() - 64, player->getY() - 580);
cone.setColor(sf::Color(255, 255, 255, 255 * fade));
app->draw(cone, sf::BlendAdd);
}
app->draw(uiSprites.gui);
EntityManager::getInstance().renderAfter(app, 5000);
if (gameState == gameStatePlayingDisplayBoss)
{
// boss name
int fade = 255;
if (bossDisplayTimer < 0.25f)
{
fade = 255 - (0.25f - bossDisplayTimer) * 1000;
}
else if (bossDisplayTimer > PORTRAIT_DIAPLAY_TIME - 0.25f)
{
fade = (PORTRAIT_DIAPLAY_TIME - bossDisplayTimer) * 1000;
if (fade < 0) fade = 0;
}
write("THE COOK", 27, GAME_WIDTH / 2, 540, ALIGN_CENTER, sf::Color(255, 255, 255, fade), app, 0, 0, 0);
}
}
void WitchBlastGame::renderScore()
{
if (scoreDisplayed < score - 300) scoreDisplayed += 100;
else if (scoreDisplayed < score) scoreDisplayed++;
float x = 24, y = 19;
int scoretemp = scoreDisplayed;
int n = scoretemp / 10000;
uiSprites.numberSprite.setTextureRect(sf::IntRect(n * 20, 0, 20, 28));
uiSprites.numberSprite.setPosition(x , y);
app->draw(uiSprites.numberSprite);
scoretemp %= 10000;
n = scoretemp / 1000;
uiSprites.numberSprite.setTextureRect(sf::IntRect(n * 20, 0, 20, 28));
uiSprites.numberSprite.setPosition(x + 17, y);
app->draw(uiSprites.numberSprite);
scoretemp %= 1000;
n = scoretemp / 100;
uiSprites.numberSprite.setTextureRect(sf::IntRect(n * 20, 0, 20, 28));
uiSprites.numberSprite.setPosition(x + 2 * 17, y);
app->draw(uiSprites.numberSprite);
scoretemp %= 100;
n = scoretemp / 10;
uiSprites.numberSprite.setTextureRect(sf::IntRect(n * 20, 0, 20, 28));
uiSprites.numberSprite.setPosition(x + 3 * 17, y);
app->draw(uiSprites.numberSprite);
scoretemp %= 10;
n = scoretemp;
uiSprites.numberSprite.setTextureRect(sf::IntRect(n * 20, 0, 20, 28));
uiSprites.numberSprite.setPosition(x + 4 * 17, y);
app->draw(uiSprites.numberSprite);
if (scoreBonusTimer > 0.0f)
{
sf::Color color;
if (scoreBonusTimer < 1.0f) color = sf::Color(255, 255, 255, 255 * scoreBonusTimer);
else color = sf::Color::White;
write(scoreBonus, 13, 116, 51, ALIGN_RIGHT,color, app, 0, 0, 116);
scoreBonusTimer -= deltaTime;
}
}
void WitchBlastGame::renderLifeBar()
{
if (lifeBar.toDisplay)
{
int label_dy = 0;
int xBarOffset = 100;
float l = lifeBar.hp * ((MAP_WIDTH - 1) * TILE_WIDTH - xBarOffset) / lifeBar.hpMax;
sf::RectangleShape rectangle(sf::Vector2f((MAP_WIDTH - 1) * TILE_WIDTH - xBarOffset, 24));
rectangle.setFillColor(sf::Color(0, 0, 0,128));
rectangle.setPosition(sf::Vector2f(TILE_WIDTH / 2 + xBarOffset, label_dy + 22));
rectangle.setOutlineThickness(1);
rectangle.setOutlineColor(sf::Color(200, 200, 200, 200));
app->draw(rectangle);
rectangle.setSize(sf::Vector2f(l, 24));
rectangle.setFillColor(sf::Color(190, 20, 20));
rectangle.setOutlineThickness(0);
//rectangle.setPosition(sf::Vector2f(TILE_WIDTH / 2, label_dy + 22));
app->draw(rectangle);
game().write( lifeBar.label,
18,
TILE_WIDTH / 2 + 10.0f + xBarOffset,
label_dy + 22,
ALIGN_LEFT,
sf::Color(255, 255, 255),
app, 0 , 0, 0);
}
}
void WitchBlastGame::renderRunningGame()
{
EntityManager::getInstance().sortByZ();
getCurrentMapEntity()->computeBoltParticulesVertices();
if (!isPlayerAlive)
{
sf::View view = app->getView();
sf::View viewSave = app->getView();
float endAge = player->getEndAge();
if (!parameters.zoom || endAge > 4.0f)
{
// do nothing
}
else if (endAge > 3.0f)
{
view.zoom(1.0f - 0.75f * (4.0f - endAge));
float xDiff = view.getCenter().x - player->getX();
float yDiff = view.getCenter().y - player->getY();
view.setCenter(view.getCenter().x - xDiff * (4.0f - endAge),
view.getCenter().y - yDiff * (4.0f - endAge));
}
else if (endAge > 1.0f)
{
view.zoom(0.25f);
view.setCenter(player->getX(), player->getY());
}
else
{
view.zoom(1.0f - 0.75f * endAge);
float xDiff = view.getCenter().x - player->getX();
float yDiff = view.getCenter().y - player->getY();
view.setCenter(view.getCenter().x - xDiff * endAge,
view.getCenter().y - yDiff * endAge);
}
view.move(-5, -5);
app->setView(view);
renderGame();
app->setView(viewSave);
renderHud();
}
else if (parameters.zoom && gameTime < 1.0f)
{
sf::View view = app->getView();
sf::View viewSave = app->getView();
view.zoom(0.25f + 0.75f * (gameTime));
view.move(-5, -5);
app->setView(view);
renderGame();
app->setView(viewSave);
renderHud();
}
else if (xGame[xGameTypeShake].active)
{
sf::View view = app->getView();
sf::View viewSave = app->getView();
view.move(-4 + rand() % 9, -4 + rand() % 9);
app->setView(view);
renderGame();
app->setView(viewSave);
renderHud();
}
else
{
sf::View view = app->getView();
sf::View viewSave = app->getView();
view.move(-OFFSET_X, -OFFSET_Y);
app->setView(view);
renderGame();
app->setView(viewSave);
renderHud();
}
sf::RectangleShape rectangle(sf::Vector2f(200, 25));
// effects
if (player->isSpecialStateActive(SpecialStateTime))
{
sf::RectangleShape whiteLine = sf::RectangleShape(sf::Vector2f(GAME_WIDTH, 2));
whiteLine.setFillColor(sf::Color(255, 255, 255, 32));
for (int i = 0; i < 8; i++)
{
whiteLine.setPosition(xOffset, yOffset + rand() % GAME_HEIGHT);
app->draw(whiteLine);
}
}
if (isPressing(0, KeyTimeControl, false) && gameState == gameStatePlaying)
{
// effect
int effectFade = 10 + 20 * (1.0f + cos(12.0f * getAbsolutTime())) * 0.5f;
rectangle.setFillColor(sf::Color(0, 255, 255, effectFade));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
sf::RenderStates r;
r.blendMode = sf::BlendAlpha ;
app->draw(rectangle, r);
}
if (xGame[xGameTypeFade].active && xGame[xGameTypeFade].param == X_GAME_FADE_IN)
{
// fade in
rectangle.setFillColor(sf::Color(0, 0, 0, 255 - ((FADE_IN_DELAY - xGame[xGameTypeFade].timer) / FADE_IN_DELAY) * 255));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
}
else if (xGame[xGameTypeFade].active && xGame[xGameTypeFade].param == X_GAME_FADE_OUT)
{
// fade out
rectangle.setFillColor(sf::Color(0, 0, 0, ((FADE_IN_DELAY - xGame[xGameTypeFade].timer) / FADE_IN_DELAY) * 255));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
}
if (player->isSpecialStateActive(SpecialStateConfused))
{
specialStateStuct specialState = player->getSpecialState(SpecialStateConfused);
// effect
int effectFade = 150 + cos(3.0f * getAbsolutTime()) * 100;
// fade
int fade = 55;
if (specialState.timer < 0.4f)
fade = 55 - (0.4f - specialState.timer) / 0.4f * 55;
else if (specialState.timer > specialState.param1 - 0.4f)
fade = (specialState.param1 - specialState.timer) / 0.4f * 55;
if (fade < 0) fade = 0;
else if (fade > 55) fade = 55;
fade *= 1.5f;
rectangle.setFillColor(sf::Color(255 - effectFade, 0, effectFade, fade));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
sf::RenderStates r;
r.blendMode = sf::BlendAlpha ;
app->draw(rectangle, r);
}
else if (xGame[xGameTypeFadeColor].active)
{
// color fade
unsigned int r = 0, g = 0, b = 0;
switch (xGame[xGameTypeFadeColor].param)
{
case X_GAME_COLOR_RED:
r = 255;
g = 100;
break;
case X_GAME_COLOR_GREEN:
g = 255;
break;
case X_GAME_COLOR_BLUE:
b = 255;
break;
case X_GAME_COLOR_VIOLET:
r = 255;
b = 200;
break;
case X_GAME_COLOR_BROWN:
r = 200;
b = 100;
g = 150;
break;
case X_GAME_COLOR_WHITE:
r = 255;
b = 255;
g = 255;
break;
}
int alpha = xGame[xGameTypeFadeColor].timer * 200.0f / xGame[xGameTypeFadeColor].duration;
rectangle.setFillColor(sf::Color(r, g, b, alpha));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle, sf::BlendAdd);
}
if (gameState != gameStatePlayingDisplayBoss) renderMessages();
app->draw(uiSprites.topLayer);
miniMapEntity->display(app);
std::ostringstream oss;
oss << player->getGold();
write(oss.str(), 18, 367, 619, ALIGN_CENTER, sf::Color::White, app, 0, 0, 0);
myText.setColor(sf::Color(0, 0, 0, 255));
myText.setCharacterSize(16);
oss.str("");
oss << tools::getLabel("level") << " " << level;
writeGraphic(oss.str(), 16, levelStrPosition.x, levelStrPosition.y, ALIGN_CENTER, sf::Color::Black, app, 0, 0, 0);
//if (gameState == gameStatePlaying)
{
sf::Sprite hpSprite;
hpSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_LIFE));
int hpFade = 88.0f * (float)player->getHpDisplay() / (float)player->getHpMax();
if (hpFade < 0) hpFade = 0;
else if (hpFade > 88) hpFade = 88;
hpSprite.setPosition(477, 619 + 88 - hpFade);
hpSprite.setTextureRect(sf::IntRect(0, 88 - hpFade, 88, hpFade));
app->draw(hpSprite);
oss.str("");
oss << player->getHp() << "/" << player->getHpMax();
write(oss.str(), 16, 521, 654, ALIGN_CENTER, sf::Color::White, app, 0, 0, 0);
// mana
sf::Sprite manaSprite;
manaSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_MANA));
int manaFade = player->getPercentFireDelay() * 98;
manaSprite.setPosition(10, 614 + 98 - manaFade);
manaSprite.setTextureRect(sf::IntRect(0, 98 - manaFade, 98, manaFade));
app->draw(manaSprite);
if (player->getActiveSpell().spell != SpellNone)
{
sf::Sprite spellSprite;
spellSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_SPELLS));
spellSprite.setPosition(20, 624);
int frame = player->getActiveSpell().spell;
spellSprite.setTextureRect(sf::IntRect(frame * 78, 78, 78, 78));
app->draw(spellSprite);
int spellFade = player->getPercentSpellDelay() * 78;
spellSprite.setPosition(20, 624 + 78 - spellFade);
spellSprite.setTextureRect(sf::IntRect(frame * 78, 78 - spellFade, 78, spellFade));
app->draw(spellSprite);
if (player->canCastSpell())
{
float fade = (cos(8.0f * getAbsolutTime()) + 1.0f) * 0.5f;
spellSprite .setColor(sf::Color(255, 255, 255, 255 * fade));
app->draw(spellSprite, sf::BlendAdd);
}
}
// drawing the key on the interface
if (player->isEquiped(EQUIP_BOSS_KEY)) app->draw(uiSprites.keySprite);
// drawing the level items
if (player->isEquiped(EQUIP_FLOOR_MAP))
{
sf::Sprite mapSprite;
mapSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
mapSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * 3, ITEM_HEIGHT * 4, ITEM_WIDTH, ITEM_HEIGHT));
mapSprite.setPosition(582, 647);
app->draw(mapSprite);
}
if (player->isEquiped(EQUIP_ALCOHOL))
{
sf::Sprite alcSprite;
alcSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
alcSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * 4, ITEM_HEIGHT * 4, ITEM_WIDTH, ITEM_HEIGHT));
alcSprite.setPosition(582, 680);
app->draw(alcSprite);
}
if (player->isEquiped(EQUIP_LUCK))
{
sf::Sprite alcSprite;
alcSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
alcSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * 5, ITEM_HEIGHT * 4, ITEM_WIDTH, ITEM_HEIGHT));
alcSprite.setPosition(615, 680);
app->draw(alcSprite);
}
if (player->isEquiped(EQUIP_FAIRY_POWDER))
{
sf::Sprite alcSprite;
alcSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
alcSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * 6, ITEM_HEIGHT * 4, ITEM_WIDTH, ITEM_HEIGHT));
alcSprite.setPosition(648, 680);
app->draw(alcSprite);
}
// drawing the consumable
if (!player->isEquiped(EQUIP_BAG))
{
sf::RectangleShape shadow(sf::Vector2f(70, 33));
shadow.setFillColor(sf::Color(0, 0, 0, 128));
shadow.setPosition(sf::Vector2f(231, 614));
app->draw(shadow);
}
else
{
app->draw(uiSprites.bagSprite);
}
for (int i = 0; i < MAX_SLOT_CONSUMABLES; i++)
{
int item = player->getConsumable(i);
if (item > -1)
{
sf::Sprite consSprite;
consSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS));
consSprite.setPosition(161 + 36 * i, 615);
if (item < FirstEquipItem && item >= ItemPotion01 + NUMBER_UNIDENTIFIED)
{
int bottle = (int)(getPotion((enumItemType)item));
consSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * (bottle % 10), ITEM_HEIGHT * (bottle / 10), ITEM_WIDTH, ITEM_HEIGHT));
app->draw(consSprite);
}
consSprite.setTextureRect(sf::IntRect(ITEM_WIDTH * (item % 10), ITEM_HEIGHT * (item / 10), ITEM_WIDTH, ITEM_HEIGHT));
app->draw(consSprite);
}
}
// drawing the divinity
if (player->getDivinity().divinity >= 0)
{
sf::Sprite divSprite;
divSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_DIVINITY));
divSprite.setPosition(405, 616);
divSprite.setTextureRect(sf::IntRect(player->getDivinity().divinity * 48, 0, 48, 85));
app->draw(divSprite);
float fade = player->getFadingDivinity(false);
if (fade > 0.0f && player->getPlayerStatus() != PlayerEntity::playerStatusPraying)
{
divSprite.setTextureRect(sf::IntRect(player->getDivinity().divinity * 48, 85, 48, 85));
divSprite.setColor(sf::Color(255, 255, 255, 255 * fade));
app->draw(divSprite);
}
rectangle.setOutlineThickness(0);
if (player->getDivinity().interventions + 1 < player->getDivinity().level)
{
int fade = 50 + 50 * cosf(game().getAbsolutTime() * 8);
rectangle.setFillColor(sf::Color(100 + fade, 100 + fade, 200 + fade / 2, 255));
}
else
rectangle.setFillColor(sf::Color(100, 100, 200, 255));
rectangle.setPosition(sf::Vector2f(407, 692));
rectangle.setSize(sf::Vector2f(45 * player->getDivinity().percentsToNextLevels, 8));
app->draw(rectangle);
std::ostringstream oss;
if (player->getDivinity().level == MAX_DIVINITY_LEVEL + 1) oss << "MAX";
else oss << "lvl " << player->getDivinity().level;
write(oss.str(), 11, 429, 702, ALIGN_CENTER, sf::Color::White, app, 0, 0, 0);
}
// render the shots
renderHudShots(app);
// render PAUSE screen
if (gameState == gameStatePlayingPause)
{
// background
rectangle.setFillColor(sf::Color(0, 0, 0, 200));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH, MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
app->draw(uiSprites.pauseSprite);
float x = 588;
float y = 388;
// items
//write(tools::getLabel("inventory"), 16, x, y, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
int n = 0;
for (auto i: sortedEquipement)
{
if (i != EQUIP_BOSS_KEY && i != EQUIP_FLOOR_MAP &&
i != EQUIP_LUCK && i != EQUIP_ALCOHOL &&
i != EQUIP_FAIRY_POWDER && player->isEquiped(i))
{
sf::Sprite itemSprite;
itemSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
itemSprite.setPosition(x + (n % 8) * 40, y + (n / 8) * 40);
itemSprite.setTextureRect(sf::IntRect((i % 10) * 32, (i / 10) * 32, 32, 32));
app->draw(itemSprite);
n++;
}
}
renderInGameMenu();
// texts in the right UI part
// player name
writeGraphic(parameters.playerName, 32, 734, 52, ALIGN_CENTER, sf::Color::Black, app);
// game time
std::ostringstream oss;
oss << "Game time: ";
int time = int(gameTime);
if (time > 3600)
{
int hours = time / 3600;
oss << hours << ":";
time -= 3600 * hours;
}
int minutes = time / 60;
if (minutes < 10) oss << "0";
oss << minutes;
time -= 60 * minutes;
oss << ":";
if (time < 10) oss << "0";
oss << time;
writeGraphic(oss.str(), 18, 734, 143, ALIGN_CENTER, sf::Color::Black, app);
oss.str("");
oss << tools::getLabel("ui_base_damage") << ": " << player->getDamage();
writeGraphic(oss.str(), 17, 588, 190, ALIGN_LEFT, sf::Color::Black, app);
oss.str("");
oss << tools::getLabel("ui_fire_rate") << ": " << std::fixed << std::setprecision(1) << player->getFireRate()
<< " / " << tools::getLabel("ui_second");
writeGraphic(oss.str(), 17, 588, 222, ALIGN_LEFT, sf::Color::Black, app);
oss.str("");
oss << tools::getLabel("dc_killed_monsters") << ": " << bodyCount;
writeGraphic(oss.str(), 17, 588, 254, ALIGN_LEFT, sf::Color::Black, app);
oss.str("");
oss << tools::getLabel("dc_challenges") << ": " << challengeLevel - 1;
writeGraphic(oss.str(), 17, 588, 286, ALIGN_LEFT, sf::Color::Black, app);
oss.str("");
oss << tools::getLabel("ui_temple_donation") << ": " << player->getDonation();
writeGraphic(oss.str(), 17, 588, 318, ALIGN_LEFT, sf::Color::Black, app);
// potions
int iPotion = 0;
for (int i = 0; i < 4; i++)
{
int currentPotion = player->getConsumable(i);
if (currentPotion > -1)
{
bool ok = true;
for (int j = 0; j < i; j++) if (currentPotion == player->getConsumable(j)) ok = false;
if (ok)
{
int xPotion = 40;
int yPotion = 440 + 30 * iPotion;
sf::Sprite itemSprite;
itemSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS));
itemSprite.setPosition(xPotion, yPotion);
if (currentPotion >= ItemPotion01 + NUMBER_UNIDENTIFIED )
{
int basisPotion = getPotion(enumItemType(currentPotion));
itemSprite.setTextureRect(sf::IntRect((basisPotion % 10) * 32, (basisPotion / 10) * 32, 32, 32));
app->draw(itemSprite);
}
itemSprite.setTextureRect(sf::IntRect((currentPotion % 10) * 32, (currentPotion / 10) * 32, 32, 32));
app->draw(itemSprite);
// write(tools::getLabel(items[currentPotion].name), 13, xPotion + 37, yPotion + 11, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
// write(tools::getLabel(items[currentPotion].description), 12, xPotion + 3, yPotion + 36, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
write(tools::getLabel(items[currentPotion].description), 12, xPotion + 37, yPotion + 11, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
iPotion++;
}
}
}
}
// score TODO
renderScore();
if (player->isDead())
{
float deathAge = player->getEndAge();
if (deathAge > DEATH_CERTIFICATE_DELAY)
{
rectangle.setFillColor(sf::Color(0, 0, 0, 180));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
renderDeathScreen(80, 110);
if (scoreSaveFile.compare("") == 0)
{
write(tools::getLabel("certificate_capture"), 16, 80, 430, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
}
else
{
std::stringstream ss;
ss << tools::getLabel("certificate_saved") << " " << scoreSaveFile;
write(ss.str(), 16, 80, 430, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
}
}
else if (deathAge > DEATH_CERTIFICATE_DELAY - 1.0f)
{
rectangle.setFillColor(sf::Color(0, 0, 0, 180 * (deathAge - 2.5f)));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
renderDeathScreen(80 + (DEATH_CERTIFICATE_DELAY - deathAge) * 1000, 110);
}
}
else if (player->getPlayerStatus() == PlayerEntity::playerStatusVictorious)
{
if (player->getEndAge() > 2.0f)
{
rectangle.setFillColor(sf::Color(0, 0, 0, 180));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
renderDeathScreen(80, 110);
if (scoreSaveFile.compare("") == 0)
{
write(tools::getLabel("certificate_capture"), 16, 80, 430, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
}
else
{
std::stringstream ss;
ss << tools::getLabel("certificate_saved") << " " << scoreSaveFile;
write(ss.str(), 16, 80, 430, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
}
}
else
{
rectangle.setFillColor(sf::Color(0, 0, 0, 90 * (player->getEndAge())));
rectangle.setPosition(sf::Vector2f(xOffset, yOffset));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
renderDeathScreen(80 + (2.0f - player->getEndAge() ) * 1000, 110);
}
}
else if (player->getPlayerStatus() != PlayerEntity::playerStatusVictorious && currentMap->getRoomType() == roomTypeExit && level >= LAST_LEVEL)
{
float x0 = (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2;
write(tools::getLabel("congratulations_1"), 25, x0, 220, ALIGN_CENTER, sf::Color::White, app, 2, 2, 0);
write(tools::getLabel("congratulations_2"), 23, x0, 250, ALIGN_CENTER, sf::Color::White, app, 2, 2, 0);
write(tools::getLabel("congratulations_3"), 23, x0, 280, ALIGN_CENTER, sf::Color::White, app, 2, 2, 0);
registerAchievement(AchievementWin);
if (endingTimer < 0.0f)
{
endingTimer = ENDING_TIMER;
isBonusTimeAdded = false;
}
}
}
// show game time
if (showGameTime)
{
int minutes = (int)gameTime / 60;
int secondes = (int)gameTime % 60;
std::stringstream ss;
if (minutes < 10) ss << "0";
ss << minutes;
ss << ":";
if (secondes < 10) ss << "0";
ss << secondes;
write(ss.str(), 14, 4, 4, ALIGN_LEFT, sf::Color::Green, app, 0, 0, 0);
}
// achievements ?
if (!achievementsQueue.empty()) // && (currentMap->isCleared() || achievementsQueue.front().hasStarted) )
{
if (!achievementsQueue.front().hasStarted)
{
SoundManager::getInstance().playSound(SOUND_ACHIEVEMENT);
achievementsQueue.front().hasStarted = true;
achievementState[achievementsQueue.front().type] = AchievementDone;
saveGameData();
proceedEvent(EventAchievement);
// text
float x0 = MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = MAP_HEIGHT * 0.5f * TILE_HEIGHT + 40.0f;
TextEntity* text = new TextEntity(tools::getLabel("achievement_complete"), 30, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1200);
text->setColor(TextEntity::COLOR_FADING_WHITE);
}
int xPos = 560;
int yPos = 4;
int opening = 384;
float achievAge = ACHIEVEMENT_DELAY_MAX - achievementsQueue.front().timer;
sf::Sprite spriteScroll;
if (achievAge < 1.0f)
opening = 32;
else if (achievAge < 2.0f)
opening = 32 + (achievAge - 1.0f) * (384 - 32);
else if (achievAge > ACHIEVEMENT_DELAY_MAX - 1.0f)
opening = 32 + (ACHIEVEMENT_DELAY_MAX - achievAge) * (384 - 32);
if (achievAge < 0.5f)
spriteScroll.setColor(sf::Color(255, 255, 255, 500 * achievAge));
else if (achievAge > ACHIEVEMENT_DELAY_MAX - 0.5f)
spriteScroll.setColor(sf::Color(255, 255, 255, 500 * (ACHIEVEMENT_DELAY_MAX - achievAge)));
spriteScroll.setTexture(*ImageManager::getInstance().getImage(IMAGE_UI_ACHIEV));
spriteScroll.setTextureRect(sf::IntRect(128 + 384 - opening, 0, opening, 64));
spriteScroll.setPosition(xPos + 384 - opening, yPos);
app->draw(spriteScroll);
if ((achievAge > 1.0f && achievAge < 2.0f) || achievAge > ACHIEVEMENT_DELAY_MAX - 1.0f)
spriteScroll.setTextureRect(sf::IntRect(32 * ((int)(achievAge * 8) % 4), 0, 32, 64));
else
spriteScroll.setTextureRect(sf::IntRect(0, 0, 32, 64));
spriteScroll.setPosition(xPos + 394 - opening - 16, yPos);
app->draw(spriteScroll);
if (achievAge > 2.0f && achievAge < ACHIEVEMENT_DELAY_MAX - 1.0f)
{
sf::Sprite icon;
icon.setTexture(*ImageManager::getInstance().getImage(IMAGE_ACHIEVEMENTS));
icon.setTextureRect(sf::IntRect( ((achievementsQueue.front().type + 1) % 10) * 64,
((achievementsQueue.front().type + 1) / 10) * 64, 64, 64));
icon.setPosition(xPos + 308, yPos + 9);
icon.setScale(0.7f, 0.7f);
app->draw(icon);
icon.setColor(sf::Color(255, 255, 255, 50 + 50 * cosf(getAbsolutTime() * 4)));
app->draw(icon, sf::BlendAdd);
game().write(achievementsQueue.front().message, 13, xPos + 34, yPos + 10, ALIGN_LEFT, sf::Color::Black, app, 0, 0, 0);
if (achievements[achievementsQueue.front().type].unlockType == UnlockItem)
{
game().write(tools::getLabel(items[achievements[achievementsQueue.front().type].unlock].name),
13, xPos + 70, yPos + 34, ALIGN_LEFT, sf::Color::Black, app, 0, 0, 0);
}
else if (achievements[achievementsQueue.front().type].unlockType == UnlockFunctionality)
{
game().write(tools::getLabel(functionalityLabel[achievements[achievementsQueue.front().type].unlock]),
13, xPos + 70, yPos + 34, ALIGN_LEFT, sf::Color::Black, app, 0, 0, 0);
}
}
//if (achievAge < 1.0f)
{
int i = achievementsQueue.front().counter;
if (i < 64)
{
for (int j = 0; j < 2; j++)
{
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40, yPos - i + 64);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40, yPos - i + 64 + 1);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40, yPos - i + 64 + 2);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40, yPos - i + 64 + 3);
}
}
else if (i < 40 + 64)
{
for (int j = 0; j < 2; j++)
{
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40 + i - 64, yPos);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40 + i - 63, yPos);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40 + i - 62, yPos);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - 40 + i - 61, yPos);
}
}
else if (i < 40 + 64 + 64)
{
for (int j = 0; j < 2; j++)
{
generateUiParticle(xPos + 394 + 16 + 4 - opening, yPos + i - 40 - 64);
generateUiParticle(xPos + 394 + 16 + 4 - opening, yPos + i - 40 - 64 + 1);
generateUiParticle(xPos + 394 + 16 + 4 - opening, yPos + i - 40 - 64 + 2);
generateUiParticle(xPos + 394 + 16 + 4 - opening, yPos + i - 40 - 64 + 3);
}
}
else if (i < 40 + 64 + 40 + 64)
{
for (int j = 0; j < 2; j++)
{
generateUiParticle(xPos + 394 + 16 + 4 - 32 - i + 144, yPos + 64);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - i + 144 + 1, yPos + 64);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - i + 144 + 2, yPos + 64);
generateUiParticle(xPos + 394 + 16 + 4 - 32 - i + 144 + 3, yPos + 64);
}
}
achievementsQueue.front().counter += 4;
}
}
}
void WitchBlastGame::renderMessages()
{
int dy = 0;
// message queue
if (!messagesQueue.empty())
{
dy = 40;
if (messagesQueue.front().timer < 0.5f)
{
dy *= (2 * messagesQueue.front().timer);
}
else if (messagesQueue.front().timerMax - messagesQueue.front().timer < 0.5f)
{
dy *= (2 * (messagesQueue.front().timerMax - messagesQueue.front().timer));
}
uiSprites.msgBoxSprite.setTextureRect(sf::IntRect(0, 0, 970, dy + 2));
uiSprites.msgBoxSprite.setPosition(0, 600 - dy);
app->draw(uiSprites.msgBoxSprite);
std::stringstream ss;
ss << messagesQueue.front().message[0];
ss << ": ";
ss << messagesQueue.front().message[1];
ss << std::endl;
ss << messagesQueue.front().message[2];
write(ss.str(), 16, 10, 602 - dy, ALIGN_LEFT, sf::Color::White, app, 0, 0, 0);
}
// show player effects
if (isPlayerAlive)
{
float x0 = 18, y0 = 585, xStep = 160;
for (int i = 0; i < NB_SPECIAL_STATES; i++)
{
if ( (player->isSpecialStateActive((enumSpecialState)i) && player->getSpecialState((enumSpecialState)i).timer > 0.35f)
|| player->getSpecialState((enumSpecialState)i).waitUnclear)
{
std::stringstream oss;
oss << tools::getLabel(specialStateToLabel[i]) << " : ";
if (player->getSpecialState((enumSpecialState)i).waitUnclear)
{
oss << "waiting";
int n = (int)(getAbsolutTime() * 3) % 3;
if (n == 0) oss << ".";
else if (n == 1) oss << "..";
else oss << "...";
}
else
oss << (int)(player->getSpecialState((enumSpecialState)i).timer);
write(oss.str(), 12, x0, y0 - dy, ALIGN_LEFT, specialStateToColor[i], app, 0, 0, 0);
x0 += xStep;
}
}
}
}
void WitchBlastGame::saveDeathScreen()
{
std::stringstream ss;
ss << "rip_";
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
ss << (now->tm_year + 1900);
if (now->tm_mon < 9) ss << "0";
ss << (now->tm_mon + 1);
if (now->tm_mday < 9) ss << "0";
ss << now->tm_mday;
if (now->tm_hour <= 9) ss << "0";
ss << (now->tm_hour);
if (now->tm_min <= 9) ss << "0";
ss << (now->tm_min);
if (now->tm_sec <= 9) ss << "0";
ss << (now->tm_sec);
ss << ".png";
int width = 810, height = 300, border = 4;
int x = 80, y = 110;
sf::Image screenShot(app->capture());
sf::Image savedImage;
savedImage.create(width + border * 2, height + border * 2);
savedImage.copy(screenShot,0 , 0, sf::IntRect( x - border, y - border, width + border * 2, height + border * 2));
savedImage.saveToFile(ss.str());
}
void WitchBlastGame::saveScreen()
{
std::stringstream ss;
ss << "screenshot_";
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
ss << (now->tm_year + 1900);
if (now->tm_mon < 9) ss << "0";
ss << (now->tm_mon + 1);
if (now->tm_mday < 9) ss << "0";
ss << now->tm_mday;
if (now->tm_hour <= 9) ss << "0";
ss << (now->tm_hour);
if (now->tm_min <= 9) ss << "0";
ss << (now->tm_min);
if (now->tm_sec <= 9) ss << "0";
ss << (now->tm_sec);
ss << ".png";
sf::Image screenShot(app->capture());
screenShot.saveToFile(ss.str());
}
void WitchBlastGame::renderDeathScreen(float x, float y)
{
int xRect = 810;
sf::Sprite rectangle;
rectangle.setTexture(*ImageManager::getInstance().getImage(IMAGE_DEATH_CERTIFICATE));
rectangle.setPosition(x - 4, y - 4);
app->draw(rectangle);
std::stringstream ss;
if (player->isDead())
ss << parameters.playerName << " - " << tools::getLabel("dc_certificate");
else
{
ss << parameters.playerName << " - " << tools::getLabel("dc_victory");
sf::Sprite seal;
seal.setTexture(*ImageManager::getInstance().getImage(IMAGE_WIN_SEAL));
seal.setPosition(x + 650, y + 50);
app->draw(seal);
}
write(ss.str(), 18, x + xRect / 2, y + 5, ALIGN_CENTER, sf::Color::Black, app, 0, 0, 0);
ss.str(std::string());
ss.clear();
int minutes = (int)gameTime / 60;
if (minutes < 1) minutes = 1;
if (player->isDead())
{
ss << tools::getLabel("dc_killed_by") << " " << sourceToString(player->getLastHurtingSource(), player->getLastHurtingEnemy()) << "." << std::endl;
ss << tools::getLabel("dc_died_level") << " " << level << " " << tools::getLabel("dc_after") << " " << minutes << " " << tools::getLabel("dc_minutes") << "." << std::endl;
}
else
{
ss << tools::getLabel("dc_after_victory") << " " << minutes << " " << tools::getLabel("dc_minutes") << "." << std::endl;
}
ss << tools::getLabel("dc_killed_monsters") << ": " << bodyCount << std::endl;
ss << tools::getLabel("dc_gold") << ": " << player->getGold() << std::endl;
ss << tools::getLabel("dc_challenges") << ": " << challengeLevel - 1 << std::endl;
write(ss.str(), 16, x + 112, y + 50, ALIGN_LEFT, sf::Color::Black, app, 0, 0, 0);
// player
renderPlayer(x + 40, y + 48, player->getEquipment(), player->getShotType(), 1, 0);
// items
write(tools::getLabel("inventory"), 16, x + 14, y + 165, ALIGN_LEFT, sf::Color::Black, app, 0, 0, 0);
int n = 0;
for (auto i: sortedEquipement)
{
if (i != EQUIP_BOSS_KEY && player->isEquiped(i))
{
sf::Sprite itemSprite;
itemSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
itemSprite.setPosition(x + 14 + n * 32, y + 183);
itemSprite.setTextureRect(sf::IntRect((i % 10) * 32, (i / 10) * 32, 32, 32));
app->draw(itemSprite);
n++;
}
}
ss.str("");
ss.clear();
ss << "Score: " << score;
write(ss.str(), 24, x + xRect / 2, y + 240, ALIGN_CENTER, sf::Color::Black, app, 0, 0, 0);
}
bool compareScores(WitchBlastGame::StructScore s1, WitchBlastGame::StructScore s2)
{
return s1.score > s2.score;
}
void WitchBlastGame::calculateScore()
{
saveStats();
std::ostringstream oss;
int goldScore = getGoldScore(player->getGold());
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
if (player->isEquiped(i)) goldScore += getItemScore((item_equip_enum)i);
lastScore.equip[i] = player->isEquiped(i);
}
addBonusScore(BonusPossession, goldScore);
// time
if (!player->isDead())
{
lastScore.level = -1;
}
else
lastScore.level = level;
// to save
lastScore.name = parameters.playerName;
lastScore.score = score;
lastScore.shotType = player->getShotType();
lastScore.divinity = player->getDivinity().divinity;
lastScore.killedBy = player->getLastHurtingEnemy();
lastScore.time = (int)gameTime;
if (nbPlayers > 1) return;
scores.push_back(lastScore);
std::sort (scores.begin(), scores.end(), compareScores);
saveHiScores();
// Online
#ifdef ONLINE_MODE
- if (!gameFromSaveFile)
+ if (autosave || !gameFromSaveFile)
{
sendScoreToServer();
}
else
{
receiveScoreFromServer();
}
#endif // ONLINE_MODE
}
void WitchBlastGame::switchToMenu()
{
gameState = gameStateMenu;
buildMenu(false);
if (!config.configFileExists())
{
menuState = MenuStateFirst;
menuFirst.index = 0;
menuKeyIndex = 0;
}
}
void WitchBlastGame::updateMenu()
{
SoundManager::getInstance().playSound(SOUND_NIGHT, false);
menuStuct* menu = NULL;
bool noMenu = false;
if (menuState == MenuStateMain)
menu = &menuMain;
else if (menuState == MenuStateConfig)
menu = &menuConfig;
else if (menuState == MenuStateFirst)
menu = &menuFirst;
else
noMenu = true;
bool escape = false;
EntityManager::getInstance().animate(deltaTime);
for (int i = 0; i < 2; i++)
{
introSprites[i]->setX(introSprites[i]->getX() - deltaTime * 35);
if (introSprites[i]->getX() < - SCREEN_WIDTH / 2) introSprites[i]->setX(introSprites[i]->getX() + 2 * SCREEN_WIDTH);
}
// Process events
sf::Event event;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
saveGameData();
app->close();
}
if (event.type == sf::Event::Resized)
{
enableAA(true);
sf::View view = app->getDefaultView();
view = getLetterboxView( view, event.size.width, event.size.height );
app->setView(view);
}
if (event.type == sf::Event::TextEntered)
{
if (menuState == MenuStateChangeName)
{
if (event.text.unicode < 128)
{
char c = static_cast<char>(event.text.unicode);
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
parameters.playerName += static_cast<char>(event.text.unicode);
}
}
}
if (event.type == sf::Event::KeyPressed && menuState == MenuStateKeys)
{
bool alreadyUsed = false;
if (event.key.code == sf::Keyboard::Escape) alreadyUsed = true;
for (unsigned int i = 0; i < menuKeyIndex; i++)
if (input[i] == event.key.code) alreadyUsed = true;
// TODO more tests
if (!alreadyUsed)
{
input[menuKeyIndex] = event.key.code;
menuKeyIndex++;
if (menuKeyIndex == NumberKeys)
{
menuState = MenuStateConfig;
saveConfigurationToFile();
}
}
}
else if (menuState == MenuStateJoystick)
{
bool alreadyUsed = false;
int nbButtons = sf::Joystick::getButtonCount(0);
bool found = false;
JoystickInputStruct jInput;
// Escape = out
if (event.key.code == sf::Keyboard::Escape)
{
menuState = MenuStateConfig;
saveConfigurationToFile();
return;
}
// button pressed ?
for (int i = 0; !found && i < nbButtons; i++)
{
if (sf::Joystick::isButtonPressed(0, i))
{
jInput.isButton = true;
jInput.value = i;
jInput.axis = sf::Joystick::X;
found = true;
}
}
if (!found)
{
// axis ?
for (int i = sf::Joystick::X; i <= sf::Joystick::PovY; i++)
{
if (sf::Joystick::hasAxis(0, (sf::Joystick::Axis)i))
{
if (sf::Joystick::getAxisPosition(0, (sf::Joystick::Axis)i) < -50)
{
jInput.isButton = false;
jInput.value = -1;
jInput.axis = (sf::Joystick::Axis)i;
found = true;
}
else if (sf::Joystick::getAxisPosition(0, (sf::Joystick::Axis)i) > 50)
{
jInput.isButton = false;
jInput.value = 1;
jInput.axis = (sf::Joystick::Axis)i;
found = true;
}
}
}
}
if (found)
{
// already exist ?
for (unsigned int i = 0; i < menuKeyIndex; i++)
{
if (jInput.isButton && joystickInput[i].isButton
&& joystickInput[i].value == jInput.value)
alreadyUsed = true;
if (!jInput.isButton && !joystickInput[i].isButton
&& joystickInput[i].axis == jInput.axis
&& joystickInput[i].value == jInput.value)
alreadyUsed = true;
}
if (!alreadyUsed)
{
joystickInput[menuKeyIndex] = jInput;
menuKeyIndex++;
if (menuKeyIndex == NumberKeys)
{
menuState = MenuStateConfig;
saveConfigurationToFile();
}
}
}
}
else
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::F1)
{
saveScreen();
}
if (menuState == MenuStateChangeName)
{
if (event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::Return)
{
saveConfigurationToFile();
menuState = MenuStateMain;
}
else if (event.key.code == sf::Keyboard::BackSpace)
{
if (parameters.playerName.size() > 0)
parameters.playerName.erase(parameters.playerName.size() - 1);
}
}
else if (event.key.code == sf::Keyboard::Escape)
{
escape = true;
}
}
}
}
// END EVENT PROCESSING
if (menuState == MenuStateAchievements)
{
if (isPressing(0, KeyRight, true))
{
if (menuAchIndex % 8 < 7) menuAchIndex++;
}
else if (isPressing(0, KeyLeft, true))
{
if (menuAchIndex % 8 > 0) menuAchIndex--;
}
else if (isPressing(0, KeyDown, true))
{
if (menuAchIndex / 8 < ACHIEV_LINES) menuAchIndex += 8;
}
else if (isPressing(0, KeyUp, true))
{
if (menuAchIndex / 8 > 0) menuAchIndex -= 8;
}
else if (isPressing(0, KeyFireDown, true))
{
if (menuAchIndex / 8 >= ACHIEV_LINES) menuState = MenuStateMain;
}
if (escape) menuState = MenuStateMain;
}
else if (menuState == MenuStateCredits)
{
if (escape || isPressing(0, KeyFireDown, true)) menuState = MenuStateMain;
}
else if (menuState == MenuStateHiScores)
{
if (escape || isPressing(0, KeyFireDown, true))
{
menuScoreIndex++;
if (menuScoreIndex > 2)
{
menuState = MenuStateMain;
if (lastScore.level > 0)
{
lastScore.level = 0;
lastScore.score = 0;
playMusic(MusicIntro);
}
}
}
}
if (!noMenu)
{
if (escape)
{
saveGameData();
app->close();
}
else if (isPressing(0, KeyDown, true))
{
menu->index++;
if (menu->index == menu->items.size()) menu->index = 0;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (isPressing(0, KeyUp, true))
{
if (menu->index == 0) menu->index = menu->items.size() - 1;
else menu->index--;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (isPressing(0, KeyRight, true))
{
if (menu->items[menu->index].id == MenuLanguage)
{
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
parameters.language++;
if (parameters.language >= NB_LANGUAGES) parameters.language = 0;
if (menuState == MenuStateConfig) saveConfigurationToFile();
tools::setLanguage(languageString[parameters.language]);
buildMenu(true);
}
else if (menu->items[menu->index].id == MenuVolumeSound)
{
parameters.soundVolume = (parameters.soundVolume / 10) * 10 + 10;
if (parameters.soundVolume > 100) parameters.soundVolume = 100;
saveConfigurationToFile();
SoundManager::getInstance().setVolume(parameters.soundVolume);
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (menu->items[menu->index].id == MenuVolumeMusic)
{
parameters.musicVolume = (parameters.musicVolume / 10) * 10 + 10;
if (parameters.musicVolume > 100) parameters.musicVolume = 100;
saveConfigurationToFile();
updateMusicVolume();
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (menu->items[menu->index].id == MenuStartNew)
{
nbPlayers++;
if (nbPlayers > NB_PLAYERS_MAX) nbPlayers = 1;
if (nbPlayers > 1 && !sf::Joystick::isConnected(0)) nbPlayers = 1;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
buildMenu(true);
}
}
else if (isPressing(0, KeyLeft, true))
{
if (menu->items[menu->index].id == MenuLanguage)
{
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
parameters.language--;
if (parameters.language < 0) parameters.language = NB_LANGUAGES - 1;
if (menuState == MenuStateConfig) saveConfigurationToFile();
tools::setLanguage(languageString[parameters.language]);
buildMenu(true);
}
else if (menu->items[menu->index].id == MenuVolumeSound)
{
parameters.soundVolume = (parameters.soundVolume / 10) * 10 - 10;
if (parameters.soundVolume < 0) parameters.soundVolume = 0;
saveConfigurationToFile();
SoundManager::getInstance().setVolume(parameters.soundVolume);
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (menu->items[menu->index].id == MenuVolumeMusic)
{
parameters.musicVolume = (parameters.musicVolume / 10) * 10 - 10;
if (parameters.musicVolume < 0) parameters.musicVolume = 0;
saveConfigurationToFile();
updateMusicVolume();
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
}
else if (menu->items[menu->index].id == MenuStartNew)
{
nbPlayers--;
if (nbPlayers <= 0) nbPlayers = NB_PLAYERS_MAX;
if (nbPlayers > 1 && !sf::Joystick::isConnected(0)) nbPlayers = 1;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
buildMenu(true);
}
}
else if (isPressing(0, KeyFireDown, true))
{
switch (menu->items[menu->index].id)
{
case MenuStartNew:
- startNewGame(false, 1);
remove(SAVE_FILE.c_str());
+ startNewGame(false, 1);
break;
case MenuStartOld:
startNewGame(true, 1);
break;
case MenuKeys:
menuState = MenuStateKeys;
menuKeyIndex = 0;
break;
case MenuJoystick:
if (sf::Joystick::isConnected(0))
{
buildMenu(true);
menuState = MenuStateJoystick;
menuKeyIndex = 0;
}
else
buildMenu(true);
break;
case MenuCredits:
menuState = MenuStateCredits;
break;
case MenuHiScores:
menuState = MenuStateHiScores;
#ifdef ONLINE_MODE
menuScoreIndex = 0;
#else
menuScoreIndex = 2;
#endif
receiveScoreFromServer();
break;
case MenuAchievements:
menuState = MenuStateAchievements;
menuAchIndex = 0;
break;
case MenuPlayerName:
menuState = MenuStateChangeName;
break;
case MenuConfig:
menuState = MenuStateConfig;
break;
case MenuTutoReset:
for (int i = 0; i < NB_MESSAGES; i++) gameMessagesToSkip[i] = false;
SoundManager::getInstance().playSound(SOUND_SPELL_FREEZE);
saveGameData();
break;
case MenuConfigBack:
menuState = MenuStateMain;
break;
case MenuLanguage:
if (menuState == MenuStateFirst)
{
registerLanguage();
if (parameters.playerName.compare("") == 0 )
{
menuMain.index = 0;
menuState = MenuStateChangeName;
}
else
menuState = MenuStateMain;
}
break;
case MenuExit:
saveGameData();
app->close();
break;
case MenuVolumeSound:
case MenuVolumeMusic:
break;
case MenuContinue:
case MenuSaveAndQuit:
std::cout << "[ERROR] Bad Menu ID\n";
break;
}
}
}
}
void WitchBlastGame::renderMenu()
{
if (menuState == MenuStateCredits)
{
renderCredits();
return;
}
else if (menuState == MenuStateHiScores)
{
if (menuScoreIndex == 0)
renderScores(scoresOnline, "Best Players (ON-LINE)", true);
else if (menuScoreIndex == 1)
renderScores(scoresOnlineDay, "Best TODAY Scores (ON-LINE)", true);
else
renderScores(scores, "Best Scores (local)", false);
return;
}
else if (menuState == MenuStateAchievements)
{
renderAchievements();
return;
}
app->draw(introScreenSprite);
if (titleSprite.getPosition().y > 160) titleSprite.move(0, -8);
else if (titleSprite.getPosition().y < 160) titleSprite.setPosition(SCREEN_WIDTH / 2 - 15, 160);
app->draw(titleSprite);
EntityManager::getInstance().render(app);
menuStuct* menu = nullptr;
if (menuState == MenuStateMain || menuState == MenuStateChangeName)
menu = &menuMain;
else if (menuState == MenuStateConfig)
menu = &menuConfig;
else if (menuState == MenuStateFirst)
menu = &menuFirst;
int xAlign = 290;
int yTop = 320;
int yStep = 40;
if (menuState == MenuStateKeys)
{
// menu keys
if (config.configFileExists())
write(tools::getLabel("key_configuration"), 18, xAlign, 295, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
else
write(tools::getLabel("key_configuration_desc"), 18, xAlign, 295, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
for (unsigned int i = 0; i < NumberKeys; i++)
{
sf::Color itemColor;
if (menuKeyIndex == i) itemColor = sf::Color(255, 255, 255, 255);
else itemColor = sf::Color(180, 180, 180, 255);
std::ostringstream oss;
oss << tools::getLabel(inputKeyString[i]) << ": ";
if (menuKeyIndex == i) oss << tools::getLabel("key_configuration_insert");
else if (menuKeyIndex > i) oss << keyToString(input[i]);
write(oss.str(), 16, xAlign, 330 + i * 25, ALIGN_LEFT, itemColor, app, 1, 1, 0);
}
}
else if (menuState == MenuStateJoystick)
{
// menu keys
if (config.configFileExists())
write(tools::getLabel("joystick_configuration"), 18, xAlign, 295, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
else
write(tools::getLabel("joystick_configuration_desc"), 18, xAlign, 295, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
for (unsigned int i = 0; i < NumberKeys; i++)
{
sf::Color itemColor;
if (menuKeyIndex == i) itemColor = sf::Color(255, 255, 255, 255);
else itemColor = sf::Color(180, 180, 180, 255);
std::ostringstream oss;
oss << tools::getLabel(inputKeyString[i]) << ": ";
if (menuKeyIndex == i) oss << tools::getLabel("joystick_configuration_insert");
else if (menuKeyIndex > i) oss << "OK";
write(oss.str(), 16, xAlign, 330 + i * 25, ALIGN_LEFT, itemColor, app, 1, 1, 0);
}
}
else
{
// menu
for (unsigned int i = 0; i < menu->items.size(); i++)
{
sf::Color itemColor;
if (menu->index == i)
{
itemColor = sf::Color(255, 255, 255, 255);
sf::Sprite fairySprite;
fairySprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
fairySprite.setTextureRect(sf::IntRect( 48 * ((int)(20 *getAbsolutTime()) % 2), fairySpriteOffsetY, 48, 48));
fairySprite.setPosition(xAlign - 60, yTop - 10 + i * yStep + 5 * cos( 6 * getAbsolutTime()));
app->draw(fairySprite);
}
else itemColor = sf::Color(120, 120, 120, 255);
std::string label = menu->items[i].label;
if (menu->items[i].id == MenuLanguage)
{
std::ostringstream oss;
oss << label << " : " << tools::getLabel(languageString[parameters.language]);
oss << languageState[parameters.language];
label = oss.str();
}
else if (menu->items[i].id == MenuPlayerName)
{
std::ostringstream oss;
oss << label << " : " << parameters.playerName;
if (menuState == MenuStateChangeName && (int)(getAbsolutTime() * 3) % 2 == 0) oss << "_";
label = oss.str();
}
else if (menu->items[i].id == MenuVolumeSound)
{
std::ostringstream oss;
oss << label << " : ";
if (parameters.soundVolume == 0) oss << "OFF";
else oss << parameters.soundVolume;
label = oss.str();
}
else if (menu->items[i].id == MenuVolumeMusic)
{
std::ostringstream oss;
oss << label << " : ";
if (parameters.musicVolume == 0) oss << "OFF";
else oss << parameters.musicVolume;
label = oss.str();
}
write(label, 21, xAlign, yTop + i * yStep, ALIGN_LEFT, itemColor, app, 1, 1, 0);
}
write(menu->items[menu->index].description, 18, xAlign,
yTop + menu->items.size() * yStep + 8, ALIGN_LEFT, sf::Color(60, 80, 220), app, 0, 0, 0);
// Keys
if (menuState == MenuStateFirst)
{
// displaying the standard key configuration
int xKeys = 270;
int yKeys = 380;
sf::Sprite keysSprite;
if (parameters.language == 1) // french
keysSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_KEYS_AZER));
else
keysSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_KEYS_QWER));
keysSprite.setPosition(xKeys, yKeys);
app->draw(keysSprite);
// legend
write(tools::getLabel("keys_move"), 16, xKeys + 190, yKeys + 10, ALIGN_LEFT, sf::Color::White, app, 1, 1, 0);
write(tools::getLabel("keys_time"), 16, xKeys + 295, yKeys + 14, ALIGN_LEFT, sf::Color::White, app, 1, 1, 0);
write(tools::getLabel("keys_fire"), 16, xKeys + 360, yKeys + 54, ALIGN_LEFT, sf::Color::White, app, 1, 1, 0);
write(tools::getLabel("key_spell"), 16, xKeys + 148, yKeys + 184, ALIGN_CENTER, sf::Color::White, app, 1, 1, 0);
// TODO key interact
std::ostringstream oss;
oss << tools::getLabel("keys_select_1") << std::endl << tools::getLabel("keys_select_2");
write(oss.str(), 16, xKeys + 4, yKeys + 100, ALIGN_LEFT, sf::Color::White, app, 1, 1, 0);
}
}
std::ostringstream oss;
oss << APP_NAME << " v" << APP_VERSION << " - 2014-2015 - " << " Seby (code), Pierre \"dejam0rt\" Baron (2D art)";
write(oss.str(), 17, 5, 680, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
}
void WitchBlastGame::renderAchievements()
{
app->draw(introScreenSprite);
if (titleSprite.getPosition().y > 160) titleSprite.move(0, -8);
else if (titleSprite.getPosition().y < 160) titleSprite.setPosition(SCREEN_WIDTH / 2 - 15, 180);
app->draw(titleSprite);
// achievements
write(tools::getLabel("menu_achievements"), 30, 485, 280, ALIGN_CENTER, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
int achWidth = 64, achHeight = 64, x0 = 180, y0 = 380, xStep = 16, yStep = 16, nbProLine = 8;
if (menuAchIndex / 8 < ACHIEV_LINES)
{
sf::RectangleShape rectangle(sf::Vector2f(achWidth, achHeight));
rectangle.setPosition(x0 + (menuAchIndex % nbProLine) * (achWidth + xStep), y0 + (menuAchIndex / nbProLine) * (achHeight + yStep));
rectangle.setOutlineThickness(3);
rectangle.setOutlineColor(sf::Color(50, 255, 50));
app->draw(rectangle);
}
sf::Sprite sprite;
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ACHIEVEMENTS));
for (int k : sortedAchievements)
{
int i = sortedAchievements[k];
sprite.setPosition(x0 + (k % nbProLine) * (achWidth + xStep), y0 + (k / nbProLine) * (achHeight + yStep));
if (achievementState[i] == AchievementDone)
{
sprite.setTextureRect(sf::IntRect( ((i + 1) % 10) * achWidth, ((i + 1) / 10) * achHeight, achWidth, achHeight));
}
else
{
sprite.setTextureRect(sf::IntRect(0, 0, achWidth, achHeight));
}
app->draw(sprite);
}
if (menuAchIndex / 8 >= ACHIEV_LINES)
{
write(tools::getLabel("config_back"), 17, 485, 620, ALIGN_CENTER, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
}
else
{
write(tools::getLabel("config_back"), 17, 485, 620, ALIGN_CENTER, sf::Color(180, 180, 180, 255), app, 1, 1, 0);
sf::Color fontColor = sf::Color::White;
std::stringstream oss;
int achIndex = sortedAchievements[menuAchIndex];
if (!achievementState[achIndex] == AchievementDone && (
achIndex == AchievementGiantSlime
|| achIndex == AchievementCyclops
|| achIndex == AchievementRatKing
|| achIndex == AchievementGiantSpider
|| achIndex == AchievementFrancky
|| achIndex == AchievementVampire)
)
oss << "???";
else
oss << tools::getLabel(achievements[achIndex].label);
oss << ": ";
if (achievementState[achIndex] == AchievementDone)
{
oss << tools::getLabel(achievements[achIndex].label + "_desc");
if (achievements[achIndex].unlockType == UnlockItem && achievements[achIndex].unlock > -1)
oss << "\nUNLOCK: " << tools::getLabel(items[achievements[achIndex].unlock].name);
else if (achievements[achIndex].unlockType == UnlockFunctionality && achievements[achIndex].unlock > -1)
oss << "\nUNLOCK: " << tools::getLabel(functionalityLabel[achievements[achIndex].unlock]);
}
else
{
if (isFunctionalityLocked(FunctionalityAllAchievements))
oss << "???";
else
oss << tools::getLabel(achievements[achIndex].label + "_desc");
fontColor = sf::Color(150, 150, 150);
}
write(oss.str(), 19, 100, 650, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
}
}
void WitchBlastGame::renderCredits()
{
app->draw(introScreenSprite);
if (titleSprite.getPosition().y > 160) titleSprite.move(0, -8);
else if (titleSprite.getPosition().y < 160) titleSprite.setPosition(SCREEN_WIDTH / 2 - 15, 180);
app->draw(titleSprite);
// credits
write(tools::getLabel("credits"), 30, 485, 275, ALIGN_CENTER, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
int yCursorInit = 365;
int yStep = 30;
int xLeft = 30;
int xRight = 470;
int xMarging = 20;
int yCursor = yCursorInit;
write("Code", 22, xLeft, yCursor, ALIGN_LEFT, sf::Color(210, 210, 255, 255), app, 0, 0, 0);
yCursor += yStep;
int i = 0;
while (creditsCode[i] != "END")
{
write(creditsCode[i], 19, xLeft + xMarging, yCursor, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i++;
}
yCursor += yStep;
write("2D Art", 22, xLeft, yCursor, ALIGN_LEFT, sf::Color(210, 210, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i = 0;
while (credits2D[i] != "END")
{
write(credits2D[i], 19, xLeft + xMarging, yCursor, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i++;
}
yCursor += yStep;
write("Sound", 22, xLeft, yCursor, ALIGN_LEFT, sf::Color(210, 210, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i = 0;
while (creditsSound[i] != "END")
{
write(creditsSound[i], 19, xLeft + xMarging, yCursor, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i++;
}
yCursor += yStep;
////// RIGHT
yCursor = yCursorInit + yStep;
write("Music", 22, xRight, yCursor, ALIGN_LEFT, sf::Color(210, 210, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i = 0;
while (creditsMusic[i] != "END")
{
write(creditsMusic[i], 19, xRight + xMarging, yCursor, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i++;
}
yCursor += yStep;
write("Translation", 22, xRight, yCursor, ALIGN_LEFT, sf::Color(210, 210, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i = 0;
while (creditsTranslate[i] != "END")
{
write(creditsTranslate[i], 19, xRight + xMarging, yCursor, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
yCursor += yStep;
i++;
}
yCursor += yStep;
}
void WitchBlastGame::renderScores(std::vector <StructScore> scoresToRender, std::string title, bool blinkingName)
{
sf::Sprite bgSprite;
bgSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_HALL_OF_FAME));
app->draw(bgSprite);
// hi-scores-title
writeGraphic(title, 30, 485, 60, ALIGN_CENTER, sf::Color(0, 0, 0, 255), app, 0, 0, 0);
int xEquip = 540;
//int yEquip = 360;
int xPlayerLeft = 60;
int xPlayerRight = 850;
int xName = 260;
int xTime = 420;
int xLevel = 480;
int xScore = 180;
int y0 = 130;
int yStep = 50;
sf::Sprite itemSprite;
for (unsigned int i = 0; i < scoresToRender.size() && i < SCORES_MAX; i++)
{
sf::Color color = sf::Color( 15, 15, 15);
if (scoresToRender[i].score == lastScore.score
&& scoresToRender[i].level == lastScore.level
&& scoresToRender[i].name == lastScore.name
&& scoresToRender[i].time == lastScore.time)
{
int fade = 1 + cosf(getAbsolutTime() * 8) * 63;
color = sf::Color(255 - fade, 128, 255 - fade);
}
else if (blinkingName && scoresToRender[i].name == parameters.playerName)
{
int fade = 70 + sinf(getAbsolutTime() * 4) * 70;
color = sf::Color(fade * 1.5, fade, 0);
}
// equipment
int n = 0;
for (auto ii: scoreEquipement)
if (ii != EQUIP_BOSS_KEY && scoresToRender[i].equip[ii]) n++;
int dx = 30;
if (n > 14) dx = 16;
else if (n > 8) dx = 20;
n = 0;
itemSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP));
for (auto ii: scoreEquipement)
{
if (ii != EQUIP_BOSS_KEY && scoresToRender[i].equip[ii])
{
itemSprite.setPosition(xEquip + n * dx, y0 + 22 + yStep * i);
itemSprite.setTextureRect(sf::IntRect((ii % 10) * 32, (ii / 10) * 32, 32, 32));
app->draw(itemSprite);
n++;
}
}
if (i < 2)
renderPlayer((i % 2 == 0) ? xPlayerLeft : xPlayerRight, y0 - 14 + yStep * i, scoresToRender[i].equip, scoresToRender[i].shotType, 2, 7);
else if (i >= 8)
renderPlayer((i % 2 == 0) ? xPlayerLeft : xPlayerRight, y0 - 14 + yStep * i, scoresToRender[i].equip, scoresToRender[i].shotType, 0, 8);
else
renderPlayer((i % 2 == 0) ? xPlayerLeft : xPlayerRight, y0 - 14 + yStep * i, scoresToRender[i].equip, scoresToRender[i].shotType, 1, 0);
write(scoresToRender[i].name, 16, xName, y0 + 28 + yStep * i, ALIGN_LEFT, color, app, 0, 0, 0);
if (scoresToRender[i].level == -1)
{
itemSprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_WIN_SEAL_HOF));
itemSprite.setTextureRect(sf::IntRect(0, 0, 48, 48));
itemSprite.setPosition(475, y0 + 20 + yStep * i);
app->draw(itemSprite);
}
else
{
std::stringstream levelSS;
levelSS << "lvl " << scoresToRender[i].level;
write(levelSS.str(), 16, xLevel, y0 + 28 + yStep * i, ALIGN_LEFT, color, app, 0, 0, 0);
write(intToString(scoresToRender[i].score), 17, xScore, y0 + 28 + yStep * i, ALIGN_LEFT, color, app, 0, 0, 0);
}
std::stringstream timeSS;
if (scoresToRender[i].time < 100)
{
timeSS << scoresToRender[i].time << " s";
}
else
{
int minutes = scoresToRender[i].time / 60;
if (minutes < 1) minutes = 1;
timeSS << minutes << " m";
}
write(timeSS.str(), 16, xTime, y0 + 28 + yStep * i, ALIGN_LEFT, color, app, 0, 0, 0);
write(intToString(scoresToRender[i].score), 17, xScore, y0 + 28 + yStep * i, ALIGN_LEFT, color, app, 0, 0, 0);
}
// retrieving from DB ?
if ( (menuScoreIndex == 0 && scoreState == ScoreLoading)
|| (menuScoreIndex == 1 && ((scoreState == ScoreLoading) || (scoreState == ScoreLoadingDay))))
{
std::stringstream oss;
oss << "Loading";
int n = (int)(getAbsolutTime() * 3) % 3;
if (n == 0) oss << ".";
else if (n == 1) oss << "..";
else oss << "...";
write(oss.str(), 15, 20, 20, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1, 0);
}
}
void WitchBlastGame::renderInGameMenu()
{
menuStuct* menu = &menuInGame;
int xAlign = 90;
int yAlign = 100;
{
// menu
for (unsigned int i = 0; i < menu->items.size(); i++)
{
sf::Color itemColor;
if (menu->index == i)
{
itemColor = sf::Color(255, 255, 255, 255);
sf::Sprite fairySprite;
fairySprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
fairySprite.setTextureRect(sf::IntRect( 48 * ((int)(8 *getAbsolutTime()) % 2), fairySpriteOffsetY, 48, 48));
fairySprite.setPosition(xAlign - 60, yAlign + i * 90 + 5 * cos( 6 * getAbsolutTime()));
app->draw(fairySprite);
}
else itemColor = sf::Color(120, 120, 120, 255);
std::string label = menu->items[i].label;
write(label, 23, xAlign, yAlign + 10 + i * 90, ALIGN_LEFT, itemColor, app, 1, 1, 0);
write(menu->items[i].description, 15, xAlign, yAlign + i * 90 + 50, ALIGN_LEFT, itemColor, app, 0, 0, 0);
}
}
}
void WitchBlastGame::startGame()
{
lastTime = getAbsolutTime();
prepareIntro();
// Start game loop
while (app->isOpen())
{
deltaTime = getAbsolutTime() - lastTime;
if (deltaTime < 0.008f)
{
float sleepTime = 1.0f / 60.0f - deltaTime;
sf::sleep(sf::seconds(sleepTime));
deltaTime = getAbsolutTime() - lastTime;
}
lastTime = getAbsolutTime();
if (deltaTime > 0.05f) deltaTime = 0.05f;
if (app->hasFocus())
{
updateActionKeys();
switch (gameState)
{
case gameStateInit:
case gameStateKeyConfig:
case gameStateJoystickConfig:
case gameStateMenu:
updateMenu();
break;
case gameStateIntro:
updateIntro();
break;
case gameStatePlaying:
case gameStatePlayingPause:
case gameStatePlayingDisplayBoss:
updateRunningGame();
break;
}
}
else
{
// Process events
sf::Event event;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
saveGameData();
app->close();
}
if (event.type == sf::Event::LostFocus)
{
if (parameters.pauseOnFocusLost && gameState == gameStatePlaying && !player->isDead())
gameState = gameStatePlayingPause;
}
}
}
onRender();
}
quitGame();
}
void WitchBlastGame::createFloor()
{
// TODO : extracts from createNewGame
}
void WitchBlastGame::closeDoors()
{
if (!currentMap->isCleared())
{
int i;
for(i = 0; i < MAP_WIDTH; i++)
{
if (currentMap->isDoor(i, 0)) currentMap->closeDoor(i, 0);
if (currentMap->isDoor(i, MAP_HEIGHT - 1)) currentMap->closeDoor(i, MAP_HEIGHT - 1);
}
for(i = 0; i < MAP_HEIGHT; i++)
{
if (currentMap->isDoor(0, i)) currentMap->closeDoor(0, i);
if (currentMap->isDoor(MAP_WIDTH - 1, i)) currentMap->closeDoor(MAP_WIDTH - 1, i);
}
roomClosed = true;
}
}
void WitchBlastGame::openDoors()
{
int i;
for(i = 0; i < MAP_WIDTH; i++)
{
if (currentMap->isDoor(i, 0)) currentMap->openDoor(i, 0);
if (currentMap->isDoor(i, MAP_HEIGHT - 1)) currentMap->openDoor(i, MAP_HEIGHT - 1);
}
for(i = 0; i < MAP_HEIGHT; i++)
{
if (currentMap->isDoor(0, i)) currentMap->openDoor(0, i);
if (currentMap->isDoor(MAP_WIDTH - 1, i)) currentMap->openDoor(MAP_WIDTH - 1, i);
}
roomClosed = false;
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING);
if (currentMap->hasNeighbourUp() == 2 && !bossRoomOpened)
currentMap->closeDoor(MAP_WIDTH/2, 0);
else if (currentMap->hasNeighbourUp() == 1)
doorEntity[0]->openDoor();
if (currentMap->hasNeighbourLeft() == 2 && !bossRoomOpened)
currentMap->closeDoor(0, MAP_HEIGHT / 2);
else if (currentMap->hasNeighbourLeft() == 1)
doorEntity[1]->openDoor();
if (currentMap->hasNeighbourDown() == 2 && !bossRoomOpened)
currentMap->closeDoor(MAP_WIDTH / 2, MAP_HEIGHT - 1);
else if (currentMap->hasNeighbourDown() == 1)
doorEntity[2]->openDoor();
if (currentMap->hasNeighbourRight() == 2 && !bossRoomOpened)
currentMap->closeDoor(MAP_WIDTH - 1, MAP_HEIGHT / 2);
else if (currentMap->hasNeighbourRight() == 1)
doorEntity[3]->openDoor();
}
EnemyEntity* WitchBlastGame::getBoss()
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() == ENTITY_ENEMY_BOSS) return static_cast<EnemyEntity*> (e);
}
return NULL;
}
int WitchBlastGame::getEnemyCount()
{
int n=0;
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT) n++;
}
return n;
}
int WitchBlastGame::getUndeadCount()
{
int n=0;
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
if (enemy->canCollide() && (enemy->getEnemyType() == EnemyTypeZombie || enemy->getEnemyType() == EnemyTypeZombieDark
|| enemy->getEnemyType() == EnemyTypeGhost || enemy->getEnemyType() == EnemyTypeVampire || enemy->getEnemyType() == EnemyTypeBat_invocated) )
n++;
}
}
return n;
}
+bool WitchBlastGame::existsEffectZone()
+{
+ EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
+ EntityManager::EntityList::iterator it;
+
+ for (it = entityList->begin (); it != entityList->end ();)
+ {
+ GameEntity *e = *it;
+ it++;
+
+ if (e->getType() == ENTITY_EFFECT_ZONE) return true;
+ }
+
+ return false;
+}
+
void WitchBlastGame::killArtefactDescription()
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() == ENTITY_ARTIFACT_DESCRIPTION)
{
e->setDying(true);
}
}
}
void WitchBlastGame::animateEffects()
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() == ENTITY_EFFECT)
e->animate(deltaTime);
}
}
void WitchBlastGame::destroyUndead(int damage)
{
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
if (enemy->canCollide() && (enemy->getEnemyType() == EnemyTypeZombie || enemy->getEnemyType() == EnemyTypeZombieDark
|| enemy->getEnemyType() == EnemyTypeGhost || enemy->getEnemyType() == EnemyTypeVampire || enemy->getEnemyType() == EnemyTypeBat_invocated) )
{
enemy->hurt(BaseCreatureEntity::getHurtParams(damage, ShotTypeStandard, 0, false, SourceTypeMelee, EnemyTypeNone, false));
SpriteEntity* spriteCone = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_LIGHT_CONE),
enemy->getX(), enemy->getZ() - 290);
spriteCone->setZ(1000.0f);
spriteCone->setFading(true);
spriteCone->setAge(-1.2f);
spriteCone->setLifetime(2.4f);
spriteCone->setRenderAdd();
}
}
}
}
int WitchBlastGame::getItemsCount()
{
int n=0;
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() == ENTITY_ITEM) n++;
}
return n;
}
Vector2D WitchBlastGame::getNearestEnemy(float x, float y)
{
Vector2D target(-100.0f, -100.0f);
float distanceMin = -1.0f;
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT)
{
// enemy
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
if (enemy->canCollide())
{
float d2 = (x - enemy->getX()) * (x - enemy->getX()) + (y - enemy->getY()) * (y - enemy->getY());
if (target.x < -1.0f || d2 < distanceMin)
{
distanceMin = d2;
target.x = enemy->getX();
target.y = enemy->getY();
}
}
}
}
return target;
}
void WitchBlastGame::setDoorVisible(int n)
{
if (n >= 0 && n < 4)
doorEntity[n]->setVisible(true);
}
void WitchBlastGame::checkDoor(int doorId, roomTypeEnum roomCurrent, roomTypeEnum roomNeighbour, bool isNeighbourKnown)
{
if (roomNeighbour == roomTypeNULL)
{
doorEntity[doorId]->setVisible(false);
return;
}
doorEntity[doorId]->setVisible(isNeighbourKnown);
doorEntity[doorId]->setDoorType(currentMap->getDoorType(doorId));
if (roomNeighbour == roomTypeBoss && !bossRoomOpened)
{
doorEntity[doorId]->setOpen(false);
switch (doorId)
{
case 0:
currentMap->closeDoor(MAP_WIDTH/2, 0);
break;
case 1:
currentMap->closeDoor(0, MAP_HEIGHT / 2);
break;
case 2:
currentMap->closeDoor(MAP_WIDTH/2, MAP_HEIGHT - 1);
break;
case 3:
currentMap->closeDoor(MAP_WIDTH - 1, MAP_HEIGHT / 2);
break;
}
}
else
doorEntity[doorId]->setOpen(true);
}
void WitchBlastGame::refreshMap()
{
// clean the sprites from old map
EntityManager::getInstance().partialClean(10);
// if new map, it has to be randomized
bool generateMap = !(currentFloor->getMap(floorX, floorY)->isVisited());
currentMap = currentFloor->getAndVisitMap(floorX, floorY);
// load the map
dungeonEntity->refreshMap();
player->setMap(currentMap, TILE_WIDTH, TILE_HEIGHT, 0, 0);
refreshMinimap();
if(generateMap)
this->generateMap();
else
{
if (currentMap->getRoomType() == roomTypeMerchant)
{
new PnjEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2 - 1) * TILE_HEIGHT,
0);
}
}
// check doors
checkDoor(0, currentMap->getRoomType(), currentMap->getNeighbourUp(), currentMap->hasKnownNeighbour(North, true));
checkDoor(1, currentMap->getRoomType(), currentMap->getNeighbourLeft(), currentMap->hasKnownNeighbour(West, true));
checkDoor(2, currentMap->getRoomType(), currentMap->getNeighbourDown(), currentMap->hasKnownNeighbour(South, true));
checkDoor(3, currentMap->getRoomType(), currentMap->getNeighbourRight(), currentMap->hasKnownNeighbour(East, true));
// pet slime
if (player->isEquiped(EQUIP_PET_SLIME) && currentMap->getRoomType() != roomTypeTemple) new SlimePetEntity();
// barrels
checkDestroyableObjects();
}
void WitchBlastGame::refreshMinimap()
{
for (int j=0; j < FLOOR_HEIGHT; j++)
for (int i=0; i < FLOOR_WIDTH; i++)
{
int roomType = currentFloor->getRoom(i, j);
if (roomType > roomTypeNULL && currentFloor->getMap(i, j)->isVisited()&& currentFloor->getMap(i, j)->isKnown())
{
if (roomType == roomTypeStarting
|| roomType == roomTypeChallenge
|| roomType == roomTypeBonus
|| roomType == roomTypeKey
|| roomType == roomTypeBoss
|| roomType == roomTypeStandard
|| roomType == roomTypeSecret)
{
if ( currentFloor->getMap(i, j)->containsHealth())
miniMap->setTile(i, j, 5);
else
miniMap->setTile(i, j, roomTypeStandard);
}
else
{
if (roomType == roomTypeMerchant)
miniMap->setTile(i, j, 3);
else if (roomType == roomTypeTemple)
miniMap->setTile(i, j, 7);
else if (roomType == roomTypeSecret)
miniMap->setTile(i, j, 8);
else
miniMap->setTile(i, j, currentFloor->getRoom(i, j));
}
}
else if (roomType > roomTypeNULL && currentFloor->getMap(i, j)->isKnown())
{
switch (roomType)
{
case roomTypeBoss:
miniMap->setTile(i, j, 12);
proceedEvent(EventFindBossDoor);
break;
case roomTypeChallenge:
miniMap->setTile(i, j, 15);
proceedEvent(EventFindBossDoor);
break;
case roomTypeMerchant:
miniMap->setTile(i, j,
game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP) ? 13 : 11 );
break;
case roomTypeKey:
miniMap->setTile(i, j,
game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP) ? 14 : 11 );
break;
case roomTypeExit:
miniMap->setTile(i, j,
game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP) ? 16 : 11 );
break;
case roomTypeTemple:
miniMap->setTile(i, j,
game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP) ? 17 : 11 );
break;
case roomTypeBonus:
miniMap->setTile(i, j,
game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP) ? 2 : 11 );
break;
case roomTypeSecret:
miniMap->setTile(i, j, 18 );
break;
case roomTypeStandard:
case roomTypeStarting:
miniMap->setTile(i, j, 11);
break;
case roomTypeNULL:
miniMap->setTile(i, j, 0);
break;
}
}
else
miniMap->setTile(i, j, 0);
}
miniMap->setTile(floorX, floorY, 10);
levelStrPosition.x = 824;
levelStrPosition.y = 602;
if (miniMap->getTile(FLOOR_WIDTH / 2 - 1, 0) > 0
|| miniMap->getTile(FLOOR_WIDTH / 2, 0) > 0
|| miniMap->getTile(FLOOR_WIDTH / 2 + 1, 0) > 0 )
{
if (miniMap->getTile(0, 0) <= 0
|| miniMap->getTile(1, 0) <= 0
|| miniMap->getTile(2, 0) <= 0 )
// no place top-middle
levelStrPosition.x = 729;
else if (miniMap->getTile(FLOOR_WIDTH - 1, 0) <= 0
|| miniMap->getTile(FLOOR_WIDTH - 2, 0) <= 0
|| miniMap->getTile(FLOOR_WIDTH - 3, 0) <= 0 )
// no place top-left
levelStrPosition.x = 921;
else
// no place top
levelStrPosition.y = 594;
}
}
void WitchBlastGame::checkEntering()
{
if (!currentMap->isCleared())
{
player->setEntering();
SoundManager::getInstance().playSound(SOUND_DOOR_CLOSING);
for (int i=0; i<4; i++)
doorEntity[i]->closeDoor();
}
}
void WitchBlastGame::saveMapItems()
{
currentMap->cleanMapObjects();
// blood
std::vector <displayEntityStruct> blood = dungeonEntity->getBlood();
for (auto particle: blood)
currentMap->addSprite(ENTITY_BLOOD, particle.frame, particle.x, particle.y, particle.scale);
// corpses
std::vector <displayEntityStruct> corpses = dungeonEntity->getCorpses();
for (auto particle: corpses)
currentMap->addSprite(ENTITY_CORPSE, particle.frame, particle.x, particle.y, particle.scale);
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity* e = *it;
it++;
ItemEntity* itemEntity = dynamic_cast<ItemEntity*>(e);
ChestEntity* chestEntity = dynamic_cast<ChestEntity*>(e);
if (itemEntity != NULL && !itemEntity->getDying())
{
currentMap->addItem(itemEntity->getItemType(), itemEntity->getX(), itemEntity->getY(), itemEntity->getMerchandise());
} // endif
else if (chestEntity != NULL && !chestEntity->getDying())
{
currentMap->addChest(chestEntity->getChestType(), chestEntity->getOpened(), chestEntity->getX(), chestEntity->getY());
} // endif
} // end for
}
void WitchBlastGame::moveToOtherMap(int direction)
{
// stairs to next level
if (direction == 8 && currentMap->getRoomType() == roomTypeExit)
{
if (player->getPlayerStatus() != PlayerEntity::playerStatusGoingNext)
{
player->setLeavingLevel();
xGame[xGameTypeFade].active = true;
xGame[xGameTypeFade].param = X_GAME_FADE_OUT;
xGame[xGameTypeFade].timer = FADE_OUT_DELAY;
player->setVelocity(Vector2D(0.0f, - INITIAL_PLAYER_SPEED / 2));
// SCORE perfect
if (player->getLostHp(level) == 0) addBonusScore(BonusPerfect, getPerfectScore(level));
}
}
// go to another room
else
{
saveMapItems();
switch (direction)
{
case (4):
floorX--;
player->moveTo((MAP_WIDTH * TILE_WIDTH), player->getY());
player->move(4);
break;
case (6):
floorX++;
player->moveTo(0, player->getY());
player->move(6);
break;
case (8):
floorY--;
player->moveTo(player->getX(), MAP_HEIGHT * TILE_HEIGHT);
player->move(8);
break;
case (2):
floorY++;
player->moveTo(player->getX(), 0);
break;
}
refreshMap();
checkEntering();
currentMap->restoreMapObjects();
saveInFight.x = player->getX();
saveInFight.y = player->getY();
saveInFight.direction = direction;
saveMapItems();
- if (!currentMap->isCleared())
+ if (autosave || !currentMap->isCleared())
{
- saveGame();
+ saveGame(true);
}
}
}
void WitchBlastGame::onRender()
{
// clear the view
app->clear(sf::Color::Black);
switch (gameState)
{
case gameStateInit:
case gameStateKeyConfig:
case gameStateJoystickConfig:
case gameStateMenu:
renderMenu();
break;
case gameStatePlaying:
case gameStatePlayingPause:
case gameStatePlayingDisplayBoss:
renderRunningGame();
break;
case gameStateIntro:
renderIntro();
break;
}
app->display();
}
void WitchBlastGame::renderHudShots(sf::RenderTarget* app)
{
int xHud = 124;
int yHud = 655;
int index = 0;
for (int i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
if (i == 0 || player->getShotType(i) != ShotTypeStandard)
{
int type_shot = player->getShotType(i);
uiSprites.shotsSprite.setPosition(xHud + 55 * index, yHud);
if (index == player->getShotIndex())
{
uiSprites.shotsSprite.setTextureRect(sf::IntRect(0, 0, 48, 48));
app->draw(uiSprites.shotsSprite);
}
uiSprites.shotsSprite.setTextureRect(sf::IntRect(48 * ( 1 + type_shot), 0, 48, 48));
app->draw(uiSprites.shotsSprite);
// level
if (i > 0)
{
std::ostringstream oss;
oss << "lvl " << player->getShotLevel(i) + 1;
write(oss.str(), 10, xHud + 55 * index + 10, yHud + 48, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0, 0);
}
index++;
}
}
}
void WitchBlastGame::generateBlood(float x, float y, BaseCreatureEntity::enumBloodColor bloodColor)
{
dungeonEntity->generateBlood(x, y, bloodColor);
// double blood if the "Blood Snake" object is equipped
if (player->isEquiped(EQUIP_BLOOD_SNAKE)) dungeonEntity->generateBlood(x, y, bloodColor);
}
void WitchBlastGame::addCorpse(float x, float y, int frame)
{
dungeonEntity->addCorpse(x, y, frame);
}
void WitchBlastGame::showArtefactDescription(enumItemType itemType)
{
killArtefactDescription();
new ArtefactDescriptionEntity(itemType);
}
void WitchBlastGame::generateMap()
{
saveInFight.monsters.clear();
if (currentMap->getRoomType() == roomTypeStandard)
generateStandardMap();
else if (currentMap->getRoomType() == roomTypeBonus)
{
currentMap->setCleared(true);
Vector2D v = currentMap->generateBonusRoom();
int bonusType = getRandomEquipItem(false, false);
if (items[FirstEquipItem + bonusType].familiar > FamiliarNone)
{
new ChestEntity(v.x, v.y, ChestFairy + items[FirstEquipItem + bonusType].familiar, false);
}
else
{
new ItemEntity( (enumItemType)(FirstEquipItem + bonusType), v.x ,v.y);
}
}
else if (currentMap->getRoomType() == roomTypeKey)
{
currentMap->generateKeyRoom();
initMonsterArray();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
monsterArray[x0][y0] = true;
if (level == 1)
{
findPlaceMonsters(EnemyTypeRat, 2);
findPlaceMonsters(EnemyTypeBat, 2);
}
else if (level < 6)
{
findPlaceMonsters(EnemyTypeRat, 5);
findPlaceMonsters(EnemyTypeBat, 5);
for (int i = 2; i < level; i++)
{
if (rand()%2 == 0)findPlaceMonsters(EnemyTypeImpBlue, 1);
else findPlaceMonsters(EnemyTypeImpRed, 1);
}
}
else
{
findPlaceMonsters(EnemyTypeZombie, 5);
findPlaceMonsters(EnemyTypeBat, 5);
for (int i = 5; i < level; i++)
{
if (rand()%2 == 0)findPlaceMonsters(EnemyTypeImpBlue, 1);
else findPlaceMonsters(EnemyTypeImpRed, 1);
}
}
}
else if (currentMap->getRoomType() == roomTypeMerchant)
{
currentMap->generateMerchantRoom();
ItemEntity* item1 = new ItemEntity(
ItemHealth,
(MAP_WIDTH / 2 - 1) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item1->setMerchandise(true);
ItemEntity* item3 = new ItemEntity(
ItemHealthSmall,
(MAP_WIDTH / 2 + 1) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item3->setMerchandise(true);
int bonusType = getRandomEquipItem(true, true);
ItemEntity* item2 = new ItemEntity(
(enumItemType)(FirstEquipItem + bonusType),
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item2->setMerchandise(true);
// level item ?
if (rand() % 3 == 0)
{
int r = rand() % 4;
int item4Type = -1;
switch (r)
{
case 0:
if (!game().getPlayer()->isEquiped(EQUIP_FLOOR_MAP)) item4Type = ItemFloorMap;
break;
case 1:
if (!game().getPlayer()->isEquiped(EQUIP_ALCOHOL)) item4Type = ItemAlcohol;
break;
case 2:
if (!game().getPlayer()->isEquiped(EQUIP_LUCK)) item4Type = ItemLuck;
break;
default:
if (!game().getPlayer()->isEquiped(EQUIP_FAIRY_POWDER) && player->getFairieNumber() > 0) item4Type = ItemFairyPowder;
break;
}
if (item4Type > -1)
{
ItemEntity* item4 = new ItemEntity(
(enumItemType)(item4Type),
(MAP_WIDTH / 2 + 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item4->setMerchandise(true);
}
}
// consumable item
if (rand() % 3 == 0) // potion
{
ItemEntity* item5 = new ItemEntity(
(enumItemType)(ItemPotion01 + rand() % NUMBER_UNIDENTIFIED),
(MAP_WIDTH / 2 - 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item5->setMerchandise(true);
}
else // scroll
{
ItemEntity* item5 = new ItemEntity(
(enumItemType)ItemScrollRevelation,
(MAP_WIDTH / 2 - 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + 5);
item5->setMerchandise(true);
}
new PnjEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2 - 1) * TILE_HEIGHT,
0);
currentMap->setCleared(true);
proceedEvent(EventFindShop);
}
else if (currentMap->getRoomType() == roomTypeChallenge)
{
currentMap->generateRoomWithoutHoles(0);
if (challengeLevel == 1)
{
addMonster(EnemyTypeBubble,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else
{
addMonster(challengeLevel >= 3 ? EnemyTypeBubbleIce : EnemyTypeBubble,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 - 80,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
addMonster(challengeLevel >= 4 ? EnemyTypeBubbleGreater : EnemyTypeBubble,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 + 80,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
playMusic(MusicChallenge);
}
else if (currentMap->getRoomType() == roomTypeBoss)
{
currentMap->generateRoomWithoutHoles(0);
if (level == 1)
{
findPlaceMonsters(EnemyTypeRat, 2);
addMonster(EnemyTypeButcher,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoButcher);
}
else if (level == 2)
{
addMonster(EnemyTypeSlimeBoss,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoGiantSlime);
}
else if (level == 3)
{
addMonster(EnemyTypeCyclops,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoCyclops);
}
else if (level == 4)
{
addMonster(EnemyTypeRatKing,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoWererat);
}
else if (level == 5)
{
addMonster(EnemyTypeSpiderGiant,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoGiantSpiderBefore);
}
else if (level == 6)
{
addMonster(EnemyTypeFrancky,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoFranky);
}
else if (level == 7)
{
addMonster(EnemyTypeVampire,
(MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
testAndAddMessageToQueue(MsgInfoVampire);
}
else if (level == 8)
{
// TODO
FranckyEntity* b2 = new FranckyEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b2->setLabelDy(-530);
KingRatEntity* b3 = new KingRatEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 + 100,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b3->setLabelDy(-20);
new CyclopsEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 + 120,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
}
else // level > 6
{
GiantSpiderEntity* b1 = new GiantSpiderEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 - 100,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b1->setLabelDy(10);
GiantSlimeEntity* b2 = new GiantSlimeEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b2->setLabelDy(-510);
KingRatEntity* b3 = new KingRatEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2 + 100,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b3->setLabelDy(-20);
CyclopsEntity* b4 = new CyclopsEntity((MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
(MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
b4->setLabelDy(-540);
findPlaceMonsters(EnemyTypeCauldron, 2);
findPlaceMonsters(EnemyTypeImpBlue, 4);
}
playMusic(MusicBoss);
// boss screen
if (parameters.displayBossPortrait)
{
gameState = gameStatePlayingDisplayBoss;
bossDisplayTimer = 0.0f;
bossDisplayState = 0;
dungeonEntity->animate(0.0f);
}
}
else if (currentMap->getRoomType() == roomTypeStarting)
{
currentMap->generateRoomWithoutHoles(0);
currentMap->setCleared(true);
}
else if (currentMap->getRoomType() == roomTypeExit)
{
currentMap->generateExitRoom();
currentMap->setCleared(true);
new ChestEntity((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f),
ChestExit, false);
}
else if (currentMap->getRoomType() == roomTypeTemple)
{
currentMap->generateTempleRoom();
currentMap->setCleared(true);
proceedEvent(EventFindTemple);
}
else if (currentMap->getRoomType() == roomTypeSecret)
{
currentMap->generateSecretRoom();
if (rand() % 7 > 0)
{
new ChestEntity((TILE_WIDTH * MAP_WIDTH * 0.5f + TILE_WIDTH),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f),
ChestBasic, false);
new ChestEntity((TILE_WIDTH * MAP_WIDTH * 0.5f - TILE_WIDTH),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f),
ChestBasic, false);
}
else
{
int bonusType = getRandomEquipItem(false, false);
if (items[FirstEquipItem + bonusType].familiar > FamiliarNone)
{
new ChestEntity(TILE_WIDTH * MAP_WIDTH * 0.5f,
TILE_HEIGHT * MAP_HEIGHT * 0.5f,
ChestFairy + items[FirstEquipItem + bonusType].familiar, false);
}
else
{
new ItemEntity( (enumItemType)(FirstEquipItem + bonusType),
TILE_WIDTH * MAP_WIDTH * 0.5f,
TILE_HEIGHT * MAP_HEIGHT * 0.5f);
}
}
currentMap->setCleared(true);
// secret found text
TextEntity* text = new TextEntity(tools::getLabel("secret_found"),
25,
MAP_WIDTH * 0.5f * TILE_WIDTH,
MAP_HEIGHT * 0.5f * TILE_HEIGHT + 10.0f);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1200);
text->setColor(TextEntity::COLOR_FADING_WHITE);
SoundManager::getInstance().playSound(SOUND_SECRET);
secretsFound++;
addBonusScore(BonusSecret, getSecretScore());
}
else // "normal" room
currentMap->randomize(currentMap->getRoomType());
}
void WitchBlastGame::write(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow, int yShadow, int maxWidth)
{
myText.setString(str);
myText.setString(sf::String::fromUtf8(str.begin(), str.end()));
myText.setCharacterSize(size);
if (maxWidth > 0)
{
while (myText.getLocalBounds().width > maxWidth && size > 5)
{
size--;
myText.setCharacterSize(size);
}
}
float xFont = x;
if (align == ALIGN_CENTER)
xFont = x - myText.getLocalBounds().width / 2;
else if (align == ALIGN_RIGHT)
xFont = x - myText.getLocalBounds().width;
if (xShadow != 0 && yShadow != 0)
{
myText.setPosition(xFont + xShadow, y + yShadow);
myText.setColor(sf::Color(0, 0, 0, 255));
app->draw(myText);
}
myText.setPosition(xFont, y);
myText.setColor(color);
app->draw(myText);
}
void WitchBlastGame::writeGraphic(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow, int yShadow, int maxWidth)
{
if (parameters.language == 4) // russian
{
write(str, size, x, y, align, color, app, xShadow, yShadow, maxWidth);
}
else
{
myText.setFont(graphicsFont);
write(str, size, x, y, align, color, app, xShadow, yShadow, maxWidth);
myText.setFont(font);
}
}
void WitchBlastGame::initMonsterArray()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
monsterArray[i][j] = false;
}
void WitchBlastGame::addMonster(enemyTypeEnum monsterType, float xm, float ym)
{
StructMonster monster;
monster.id = monsterType;
monster.x = xm;
monster.y = ym;
saveInFight.monsters.push_back(monster);
switch (monsterType)
{
case EnemyTypeRat:
new RatEntity(xm, ym - 2, RatEntity::RatTypeNormal, false);
proceedEvent(EventMeetRatsOrBats);
break;
case EnemyTypeRatGreen:
new GreenRatEntity(xm, ym - 2);
proceedEvent(EventMeetRatsOrBats);
break;
case EnemyTypeRatBlack:
new BlackRatEntity(xm, ym - 5, BlackRatEntity::RatBlackTypeNormal);
break;
case EnemyTypeRatHelmet:
new RatEntity(xm, ym - 2, RatEntity::RatTypeHelmet, false);
break;
case EnemyTypeRatBlackHelmet:
new BlackRatEntity(xm, ym - 5, BlackRatEntity::RatBlackTypeHelmet);
break;
case EnemyTypeBat:
new BatEntity(xm, ym, BatStandard, false);
proceedEvent(EventMeetRatsOrBats);
break;
case EnemyTypeBatSkeleton:
new BatEntity(xm, ym, BatSkeleton, false);
break;
case EnemyTypeSnake:
new SnakeEntity(xm, ym, SnakeEntity::SnakeTypeNormal, false);
proceedEvent(EventMeetSnakes);
break;
case EnemyTypeSnakeBlood:
new SnakeEntity(xm, ym, SnakeEntity::SnakeTypeBlood, false);
proceedEvent(EventMeetSnakes);
break;
case EnemyTypeEvilFlower:
new EvilFlowerEntity(xm, ym - 2, FlowerTypeStandard);
break;
case EnemyTypeEvilFlowerIce:
new EvilFlowerEntity(xm, ym - 2, FlowerTypeIce);
break;
case EnemyTypeEvilFlowerFire:
new EvilFlowerEntity(xm, ym - 2, FlowerTypeFire);
break;
case EnemyTypeSlime:
new SlimeEntity(xm, ym, SlimeTypeStandard, false);
break;
case EnemyTypeImpRed:
new ImpEntity(xm, ym, ImpEntity::ImpTypeRed);
break;
case EnemyTypeImpBlue:
new ImpEntity(xm, ym, ImpEntity::ImpTypeBlue);
break;
case EnemyTypeSlimeRed:
new SlimeEntity(xm, ym, SlimeTypeRed, false);
break;
case EnemyTypeSlimeBlue:
new SlimeEntity(xm, ym, SlimeTypeBlue, false);
break;
case EnemyTypeSlimeViolet:
new SlimeEntity(xm, ym, SlimeTypeViolet, false);
break;
case EnemyTypePumpkin:
new PumpkinEntity(xm, ym, false);
break;
case EnemyTypeWitch:
new WitchEntity(xm, ym, WitchEntity::WitchTypeNormal);
proceedEvent(EventMeetWitches);
break;
case EnemyTypeWitchRed:
new WitchEntity(xm, ym, WitchEntity::WitchTypeRed);
proceedEvent(EventMeetWitches);
break;
case EnemyTypeCauldron:
new CauldronEntity(xm, ym, CauldronTypeStandard);
break;
case EnemyTypeCauldronElemental:
new CauldronEntity(xm, ym, CauldronTypeElemental);
break;
case EnemyTypeSpiderEgg:
new SpiderEggEntity(xm, ym, false);
break;
case EnemyTypeSpiderEgg_invocated:
new SpiderEggEntity(xm, ym, true);
break;
case EnemyTypeSpiderLittle:
new LittleSpiderEntity(xm, ym, SpiderTypeStandard, false);
break;
case EnemyTypeSpiderTarantula:
new LittleSpiderEntity(xm, ym, SpiderTypeTarantula, false);
break;
case EnemyTypeGhost:
new GhostEntity(xm, ym);
break;
case EnemyTypeZombie:
new ZombieEntity(xm, ym, false);
break;
case EnemyTypeZombieDark:
new ZombieDarkEntity(xm, ym);
break;
case EnemyTypeBogeyman:
new BogeymanEntity(xm, ym);
break;
case EnemyTypeSausage:
new SausageEntity(xm, ym, false);
break;
case EnemyTypeSpiderLittle_invocated:
new LittleSpiderEntity(xm, ym, SpiderTypeStandard, true);
break;
case EnemyTypeSpiderTarantula_invocated:
new LittleSpiderEntity(xm, ym, SpiderTypeTarantula, true);
break;
case EnemyTypeBubble:
new BubbleEntity(xm, ym, BubbleStandard, 0);
break;
case EnemyTypeBubbleIce:
new BubbleEntity(xm, ym, BubbleIce, 0);
break;
case EnemyTypeBubbleGreater:
new BubbleEntity(xm, ym, BubbleTriple, 0);
break;
case EnemyTypeSlimeLarge:
new LargeSlimeEntity(xm, ym, SlimeTypeStandard);
break;
case EnemyTypeSlimeBlueLarge:
new LargeSlimeEntity(xm, ym, SlimeTypeBlue);
break;
case EnemyTypeSlimeRedLarge:
new LargeSlimeEntity(xm, ym, SlimeTypeRed);
break;
case EnemyTypeSlimeVioletLarge:
new LargeSlimeEntity(xm, ym, SlimeTypeViolet);
break;
case EnemyTypeButcher:
new ButcherEntity(xm, ym);
break;
case EnemyTypeSlimeBoss:
new GiantSlimeEntity(xm, ym);
break;
case EnemyTypeCyclops:
new CyclopsEntity(xm, ym);
break;
case EnemyTypeRatKing:
new KingRatEntity(xm, ym);
break;
case EnemyTypeSpiderGiant:
new GiantSpiderEntity(xm, ym);
break;
case EnemyTypeFrancky:
new FranckyEntity(xm, ym);
break;
case EnemyTypeVampire:
new VampireEntity(xm, ym);
break;
default:
std::cout << "[WARNING] Enemy (" << monsterType << ") not handled in switch.\n";
}
}
void WitchBlastGame::findPlaceMonsters(enemyTypeEnum monsterType, int amount)
{
// find a suitable place
bool isMonsterFlying = monsterType == EnemyTypeBat
|| monsterType == EnemyTypeImpBlue
|| monsterType == EnemyTypeImpRed;
bool isMonsterLarge = monsterType == EnemyTypeSlimeLarge
|| monsterType == EnemyTypeSlimeBlueLarge
|| monsterType == EnemyTypeSlimeRedLarge
|| monsterType == EnemyTypeSlimeVioletLarge;
bool bOk;
int xm, ym;
float xMonster, yMonster;
for (int index = 0; index < amount; index++)
{
bOk = false;
int watchdog = 200;
while (!bOk && watchdog > 0)
{
bOk = true;
watchdog--;
if (isMonsterLarge)
{
xm = 2 +rand() % (MAP_WIDTH - 5);
ym = 2 +rand() % (MAP_HEIGHT - 5);
}
else
{
xm = 1 +rand() % (MAP_WIDTH - 3);
ym = 1 +rand() % (MAP_HEIGHT - 3);
}
if (monsterArray[xm][ym])
{
bOk = false;
}
if (bOk && !isMonsterFlying && !currentMap->isWalkable(xm, ym))
{
bOk = false;
}
if (bOk && isMonsterFlying && !currentMap->isFlyable(xm, ym))
{
bOk = false;
}
if (bOk)
{
xMonster = xm * TILE_WIDTH + TILE_WIDTH * 0.5f;
yMonster = ym * TILE_HEIGHT+ TILE_HEIGHT * 0.5f;
float dist2 = (xMonster - player->getX())*(xMonster - player->getX()) + (yMonster - player->getY())*(yMonster - player->getY());
if ( dist2 < 75000.0f)
{
bOk = false;
}
else
{
addMonster(monsterType, xMonster, yMonster);
monsterArray[xm][ym] = true;
}
}
}
}
}
bool WitchBlastGame::isAdvanced()
{
return (achievementState[AchievementVampire] == AchievementDone);
}
bool WitchBlastGame::isAdvancedLevel()
{
if (isAdvanced())
{
return (rand() % 150 < getAchievementsPercents());
}
else
return false;
}
void WitchBlastGame::generateStandardMap()
{
initMonsterArray();
saveInFight.monsters.clear();
generateStandardRoom(level, isAdvancedLevel());
}
item_equip_enum WitchBlastGame::getRandomEquipItem(bool toSale = false, bool noFairy = false)
{
std::vector<int> bonusSet;
int setSize = 0;
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
bool itemOk = true;
int eq = i + FirstEquipItem;
if (player->isEquiped(i)) itemOk = false;
if (itemOk && presentItems[i]) itemOk = false;
// TODO item already in floor
if (itemOk && toSale && !items[eq].canBeSold) itemOk = false;
if (itemOk && !toSale && !items[eq].canBeFound) itemOk = false;
if (itemOk && items[eq].level > level) itemOk = false;
if (itemOk && isItemLocked((enumItemType)eq)) itemOk = false;
if (itemOk && items[eq].requirement >= FirstEquipItem
&& !player->isEquiped(items[eq].requirement - FirstEquipItem)) itemOk = false;
if (itemOk && toSale && level == 1 && items[eq].price > 25) itemOk = false;
if (itemOk && toSale && level == 2 && items[eq].price > 35) itemOk = false;
if (itemOk && (items[eq].specialShot != ShotTypeStandard && items[eq].level < 3) && player->canGetNewShot(false))
itemOk = false;
if (itemOk && (items[eq].specialShot != ShotTypeStandard && items[eq].level >= 3) && player->canGetNewShot(true))
itemOk = false;
if (itemOk && i == EQUIP_BOOK_DUAL && (player->isEquiped(EQUIP_BOOK_DUAL_QUICK) || player->isEquiped(EQUIP_RAPID_SHOT)))
itemOk = false;
if (itemOk && i == EQUIP_BOOK_DUAL_QUICK && (player->isEquiped(EQUIP_BOOK_DUAL) || player->isEquiped(EQUIP_RAPID_SHOT)))
itemOk = false;
if (itemOk && i == EQUIP_RAPID_SHOT && (player->isEquiped(EQUIP_BOOK_DUAL_QUICK) || player->isEquiped(EQUIP_BOOK_DUAL)))
itemOk = false;
if (itemOk && noFairy && items[eq].familiar != FamiliarNone) itemOk = false;
if (itemOk)
{
int n = 0;
switch (items[eq].rarity)
{
case RarityCommon:
n = 8;
break;
case RarityUncommon:
n = 4;
break;
case RarityRare:
n = 2;
break;
case RarityVeryRare:
n = 1;
break;
}
//if (itemOk && eq == ItemPetSlime) n = 15000;
for (int j = 0; j < n; j++)
{
bonusSet.push_back(i);
setSize++;
}
}
}
int bonusType = 0;
if (setSize > 0) bonusType = bonusSet[rand() % setSize];
//addPresentItem(bonusType);
return (item_equip_enum) bonusType;
}
enumItemType WitchBlastGame::getItemSpell()
{
bool ok = false;
enumItemType item = ItemMagicianHat;
while (!ok)
{
enumCastSpell n = (enumCastSpell)(rand() % SPELL_MAX);
if (player->getActiveSpell().spell != n) ok = true;
if (ok)
{
switch (n)
{
case SpellNone:
case SpellTeleport:
item = ItemSpellTeleport;
break;
case SpellSlimeExplode:
item = ItemSpellSlimeExplode;
break;
case SpellFireball:
item = ItemSpellFireball;
break;
case SpellFreeze:
item = ItemSpellFreeze;
break;
case SpellEarthquake:
item = ItemSpellEarthquake;
break;
case SpellProtection:
item = ItemSpellProtection;
break;
case SpellWeb:
item = ItemSpellWeb;
break;
case SpellFlower:
item = ItemSpellFlower;
break;
case SpellFairy:
item = ItemSpellFairy;
break;
case SpellTime:
item = ItemSpellTime;
break;
case SpellLightning:
item = ItemSpellLightning;
break;
}
ok = !isItemLocked(item);
}
}
return item;
}
void WitchBlastGame::generateChallengeBonus(float x, float y)
{
// TODO (jumping)
// loot
if (player->getActiveSpell().spell == SpellNone || rand() % 2 == 0)
{
enumItemType spell1 = getItemSpell();
ItemEntity* spellItem = new ItemEntity(spell1, x, y);
spellItem->setVelocity(Vector2D(100.0f + rand()% 250));
if (spellItem->getVelocity().y < 0.0f) spellItem->setVelocity(Vector2D(spellItem->getVelocity().x, -spellItem->getVelocity().y));
spellItem->setViscosity(0.96f);
spellItem->startsJumping();
if (!isFunctionalityLocked(FunctionalityDoubleSpellDrop))
{
enumItemType spell2 = spell1;
while (spell2 == spell1) spell2 = getItemSpell();
ItemEntity* spellItem2 = new ItemEntity(spell2, x, y);
spellItem2->setVelocity(Vector2D(100.0f + rand()% 250));
if (spellItem2->getVelocity().y < 0.0f) spellItem2->setVelocity(Vector2D(spellItem2->getVelocity().x, -spellItem2->getVelocity().y));
spellItem2->setViscosity(0.96f);
spellItem2->startsJumping();
}
ItemEntity* healthItem1 = new ItemEntity(ItemHealthVerySmall, x, y);
healthItem1->setVelocity(Vector2D(100.0f + rand()% 250));
healthItem1->setViscosity(0.96f);
healthItem1->startsJumping();
ItemEntity* healthItem2 = new ItemEntity(ItemHealthVerySmallPoison, x, y);
healthItem2->setVelocity(Vector2D(100.0f + rand()% 250));
healthItem2->setViscosity(0.96f);
healthItem2->startsJumping();
}
else
{
ItemEntity* newItem;
if (challengeLevel < 4 || rand() % (player->isEquiped(EQUIP_LUCK) ? 5 : 8) > 0)
newItem = new ItemEntity(ItemBonusHealth, x, y);
else
newItem = new ItemEntity((enumItemType)(FirstEquipItem + getRandomEquipItem()), x, y);
if (newItem->getVelocity().y < 0.0f) newItem->setVelocity(Vector2D(newItem->getVelocity().x, -newItem->getVelocity().y));
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
newItem->startsJumping();
int gold = 2 + rand() % (player->isEquiped(EQUIP_LUCK) ? 9 : 14);
for (int i = 0; i < gold; i++)
{
ItemEntity* newItem = new ItemEntity(ItemCopperCoin, x, y);
newItem->setVelocity(Vector2D(90.0f + rand()% 150));
newItem->setViscosity(0.96f);
newItem->startsJumping();
}
}
}
void WitchBlastGame::checkInteraction()
{
interaction.active = false;
if (player->getPlayerStatus() != PlayerEntity::playerStatusPlaying) return;
if (currentMap->getRoomType() == roomTypeTemple)
{
int divinity = currentMap->getDivinity(player->getX() / TILE_WIDTH, player->getZ() / TILE_HEIGHT);
if (divinity > -1)
{
interaction.active = true;
interaction.type = InteractionTypeTemple;
interaction.id = divinity;
std::stringstream ss;
ss << "[" << keyToString(input[KeyInteract]) << "] - ";
if (player->getDivinity().divinity == divinity)
{
ss << tools::getLabel("interact_donate");
if (player->getGold() < 10)
{
ss << " ";
ss << tools::getLabel("interact_donate_fail");
}
}
else
{
ss << tools::getLabel("interact_worship");
ss << " ";
ss << tools::getLabel(divinityLabel[divinity] + "_0");
}
interaction.label = ss.str();
}
}
else if (player->getItemToBuy() != NULL)
{
interaction.active = true;
interaction.type = InteractionTypeMerchandise;
interaction.id = player->getItemToBuy()->getItemType();
std::stringstream ss;
ss << tools::getLabel(items[interaction.id].name);
ss << ": ";
ss << tools::getLabel(items[interaction.id].description);
if (player->getItemToBuy()->canBePickedUp())
{
ss << std::endl;
ss << "[" << keyToString(input[KeyInteract]) << "] - ";
ss << tools::getLabel("interact_shop");
}
interaction.label = ss.str();
}
}
void WitchBlastGame::verifyDoorUnlocking()
{
int collidingDirection = (player->getCollidingDirection());
if (collidingDirection != 5 && currentMap->isCleared() && !bossRoomOpened && player->isEquiped(EQUIP_BOSS_KEY))
{
int xt = (player->getX()) / TILE_WIDTH;
int yt = (player->getY()) / TILE_HEIGHT;
if (yt <= 1 && xt >= MAP_WIDTH / 2 - 1 && xt <= MAP_WIDTH / 2 + 1 && currentMap->hasNeighbourUp() == 2)
{
doorEntity[0]->openDoor();
currentMap->openDoor(MAP_WIDTH / 2, 0);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (yt >= MAP_HEIGHT - 2 && xt >= MAP_WIDTH / 2 - 1 &&xt <= MAP_WIDTH / 2 + 1 && currentMap->hasNeighbourDown() == 2)
{
doorEntity[2]->openDoor();
currentMap->openDoor(MAP_WIDTH / 2, MAP_HEIGHT - 1);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (xt <= 1 && yt >= MAP_HEIGHT / 2 - 1 && yt <= MAP_HEIGHT / 2 + 1 && currentMap->hasNeighbourLeft() == 2)
{
doorEntity[1]->openDoor();
currentMap->openDoor(0, MAP_HEIGHT / 2);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (xt >= MAP_WIDTH - 2 && yt >= MAP_HEIGHT / 2 - 1 && yt <= MAP_HEIGHT / 2 + 1 && currentMap->hasNeighbourRight() == 2)
{
doorEntity[3]->openDoor();
currentMap->openDoor(MAP_WIDTH - 1, MAP_HEIGHT / 2);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
}
}
void WitchBlastGame::playMusic(musicEnum musicChoice)
{
music.stop();
if (parameters.musicVolume <= 0) return;
music.setLoop(true);
music.setVolume(parameters.musicVolume * VolumeModifier / 100);
bool ok = false;
switch (musicChoice)
{
case MusicDungeon:
{
int r = currentStandardMusic;
while (r == currentStandardMusic) r = rand() % 3;
switch (r)
{
case 0: ok = music.openFromFile("media/sound/WitchBlastTheme.ogg"); break;
case 1: ok = music.openFromFile("media/sound/SavageLife.ogg"); break;
case 2: ok = music.openFromFile("media/sound/HauntedLighthouse.ogg"); break;
}
currentStandardMusic = r;
}
break;
case MusicEnding:
ok = music.openFromFile("media/sound/AmbiantMedieval.ogg");
break;
case MusicBoss:
ok = music.openFromFile("media/sound/ShowMeThePower.ogg");
break;
case MusicChallenge:
ok = music.openFromFile("media/sound/HellsFire.ogg");
break;
case MusicIntro:
ok = music.openFromFile("media/sound/WitchBlastTheme.ogg");
break;
}
if (ok)
music.play();
}
void WitchBlastGame::pauseMusic()
{
music.pause();
}
void WitchBlastGame::resumeMusic()
{
music.play();
}
void WitchBlastGame::updateMusicVolume()
{
if (music.getStatus() == sf::Music::Playing)
{
if (parameters.musicVolume == 0)
music.stop();
else
music.setVolume(parameters.musicVolume * VolumeModifier / 100);
}
else
{
if (parameters.musicVolume > 0)
{
bool ok = music.openFromFile("media/sound/wb.ogg");
music.setVolume(parameters.musicVolume * VolumeModifier / 100);
if (ok) music.play();
}
}
}
void WitchBlastGame::makeShake(float duration)
{
xGame[xGameTypeShake].active = true;
xGame[xGameTypeShake].timer = duration;
}
void WitchBlastGame::makeColorEffect(int color, float duration)
{
xGame[xGameTypeFadeColor].active = true;
xGame[xGameTypeFadeColor].param = color;
xGame[xGameTypeFadeColor].timer = duration;
xGame[xGameTypeFadeColor].duration = duration;
}
-void WitchBlastGame::saveGame()
+void WitchBlastGame::saveGame(bool autosave)
{
if (nbPlayers > 1) return;
+ if (!currentMap->isCleared() && !autosave) return;
if (player->getPlayerStatus() == PlayerEntity::playerStatusAcquire)
player->acquireItemAfterStance();
std::ofstream file(SAVE_FILE.c_str(), std::ios::out | std::ios::trunc);
int i, j, k, l;
if (file)
{
// version (for compatibility check)
file << SAVE_VERSION << std::endl;
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
file << (now->tm_year + 1900) << '-';
if (now->tm_mon < 9) file << "0";
file << (now->tm_mon + 1) << '-';
if (now->tm_mday < 9) file << "0";
file << now->tm_mday
<< std::endl;
if (now->tm_hour <= 9) file << "0";
file << (now->tm_hour) << ':';
if (now->tm_min <= 9) file << "0";
file << (now->tm_min) << std::endl;
// floor
file << level << " " << challengeLevel << " " << secretsFound << std::endl;
// game age
file << (int)gameTime << std::endl;
// player equip
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++) file << player->isEquiped(i) << " ";
file << std::endl;
file << player->getShotType() << std::endl;
int nbRooms = 0;
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
file << currentFloor->getRoom(i,j) << " ";
if (currentFloor->getRoom(i,j) > 0) nbRooms++;
}
file << std::endl;
}
// kill stats
for (i = 0; i < NB_ENEMY; i++) file << killedEnemies[i] << " ";
file << std::endl;
// potions
for(auto it = potionMap.begin(); it != potionMap.end(); ++it)
{
file << it->first << " " << it->second.effect << " " << it->second.known << std::endl;
}
// maps
if (currentMap->isCleared())
saveMapItems();
file << nbRooms << std::endl;
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
if (currentFloor->getRoom(i,j) > 0)
{
file << i << " " << j << " "
<< currentFloor->getMap(i, j)->getRoomType() << " "
<< currentFloor->getMap(i, j)->isKnown() << " "
<< currentFloor->getMap(i, j)->isVisited() << " "
<< currentFloor->getMap(i, j)->isRevealed() << " "
<< currentFloor->getMap(i, j)->isCleared() << std::endl;
if (currentFloor->getMap(i, j)->isVisited())
{
for (l = 0; l < MAP_HEIGHT; l++)
{
for (k = 0; k < MAP_WIDTH; k++)
{
int tile = currentFloor->getMap(i, j)->getTile(k, l);
file << tile << " ";
tile = currentFloor->getMap(i, j)->getObjectTile(k, l);
if (tile == MAPOBJ_DOOR_CLOSED) tile = MAPOBJ_DOOR_OPEN;
file << tile << " ";
file << currentFloor->getMap(i, j)->getLogicalTile(k, l) << " ";
}
file << std::endl;
}
// style
file << currentFloor->getMap(i, j)->getFloorOffset() << " "
<< currentFloor->getMap(i, j)->getWallType() << std::endl;
// items, etc...
std::list<DungeonMap::itemListElement> itemList = currentFloor->getMap(i, j)->getItemList();
file << itemList.size() << std::endl;
std::list<DungeonMap::itemListElement>::iterator it;
for (it = itemList.begin (); it != itemList.end ();)
{
DungeonMap::itemListElement ilm = *it;
it++;
file << ilm.type << " " << ilm.x << " " << ilm.y << " " << ilm.merch << std::endl;
}
// chests
std::list<DungeonMap::chestListElement> chestList = currentFloor->getMap(i, j)->getChestList();
file << chestList.size() << std::endl;
std::list<DungeonMap::chestListElement>::iterator itc;
for (itc = chestList.begin (); itc != chestList.end ();)
{
DungeonMap::chestListElement ilm = *itc;
itc++;
file << ilm.type << " " << ilm.x << " " << ilm.y << " " << ilm.state << std::endl;
}
// sprites
std::list<DungeonMap::spriteListElement> spriteList = currentFloor->getMap(i, j)->getSpriteList();
file << spriteList.size() << std::endl;
std::list<DungeonMap::spriteListElement>::iterator its;
for (its = spriteList.begin (); its != spriteList.end ();)
{
DungeonMap::spriteListElement ilm = *its;
its++;
file << ilm.type << " " << ilm.frame << " " << ilm.x << " " << ilm.y << " " << ilm.scale << std::endl;
}
// doors
for (int k = 0; k < 4; k++)
file << currentFloor->getMap(i, j)->getDoorType(k) << " ";
file << std::endl;
// random sprite
for (int k = 0; k < NB_RANDOM_TILES_IN_ROOM; k++)
{
file << currentFloor->getMap(i, j)->getRandomTileElement(k).type
<< " " <<currentFloor->getMap(i, j)->getRandomTileElement(k).x
<< " " << currentFloor->getMap(i, j)->getRandomTileElement(k).y
<< " " << currentFloor->getMap(i, j)->getRandomTileElement(k).rotation << std::endl;
}
}
}
}
file << std::endl;
}
// game
file << floorX << " " << floorY << std::endl;
file << bossRoomOpened << std::endl;
// boss door !
// fight ?
if (currentMap->isCleared())
{
file << false << std::endl;
}
else
{
file << true << std::endl;
file << saveInFight.x << " " << saveInFight.y << " " << saveInFight.direction << std::endl;
file << saveInFight.monsters.size();
for (auto monster : saveInFight.monsters)
file << " " << monster.id << " " << monster.x << " " << monster.y;
file << std::endl;
}
// player
file << player->getHp() << " " << player->getHpMax() << " " << player->getGold() << " " << player->getDonation() << std::endl;
// score
file << score << " " << bodyCount << std::endl;
// lost hp
for (i = 1; i <= LAST_LEVEL; i++) file << player->getLostHp(i) << " ";
file << std::endl;
// equip
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++) file << player->isEquiped(i) << " ";
file << std::endl;
file << player->getX() << " " << player->getY() << std::endl;
file << player->getShotIndex();
for (i = 0; i < SPECIAL_SHOT_SLOTS; i++) file << " " << player->getShotType(i) << std::endl;
file << player->getActiveSpell().spell << std::endl;
for (i = 0; i < MAX_SLOT_CONSUMABLES; i++)
{
file << player->getConsumable(i) << " ";
}
file << std::endl;
// divinity
file << player->getDivinity().divinity << " " << player->getDivinity().piety << " "
<< player->getDivinity().level << " " << player->getDivinity().interventions << std::endl;
// events
for (i = 0; i < NB_EVENTS; i++) file << worldEvent[i] << " ";
file << std::endl;
// special states
for (int i = 0; i < NB_SPECIAL_STATES; i++)
{
file << player->getSpecialState((enumSpecialState)i).active << " "
<< player->getSpecialState((enumSpecialState)i).waitUnclear << " "
<< player->getSpecialState((enumSpecialState)i).timer << " "
<< player->getSpecialState((enumSpecialState)i).param1 << " "
<< player->getSpecialState((enumSpecialState)i).param2 << " "
<< player->getSpecialState((enumSpecialState)i).param3 << std::endl;
}
file.close();
}
else
{
std::cerr << "[ERROR] Saving the game..." << std::endl;
}
}
bool WitchBlastGame::loadGame()
{
nbPlayers = 1;
resetPresentItems();
saveInFight.monsters.clear();
std::ifstream file(SAVE_FILE.c_str(), std::ios::in);
if (file)
{
int i, j, k, n;
// version
std::string v;
file >> v;
if (v != SAVE_VERSION)
{
file.close();
remove(SAVE_FILE.c_str());
return false;
}
// date an time
file >> v;
file >> v;
// floor
file >> level;
file >> challengeLevel;
file >> secretsFound;
file >> gameTime;
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
bool eq;
file >> eq;
}
file >> n;
currentFloor = new GameFloor(level);
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
int n;
file >> n;
currentFloor->setRoom(i, j, (roomTypeEnum)n);
}
}
// kill stats
for (int i = 0; i < NB_ENEMY; i++) file >> killedEnemies[i];
// potions
for (i = 0; i < NUMBER_UNIDENTIFIED * 2; i++)
{
int source, effect;
bool known;
file >> source >> effect >> known;
addPotionToMap((enumItemType)source, (enumItemType)effect, known);
}
// maps
int nbRooms;
file >> nbRooms;
for (k = 0; k < nbRooms; k++)
{
file >> i;
file >> j;
file >> n;
DungeonMap* iMap = new DungeonMap(currentFloor, i, j);
currentFloor->setMap(i, j, iMap);
iMap->setRoomType((roomTypeEnum)n);
bool flag;
file >> flag;
iMap->setKnown(flag);
file >> flag;
iMap->setVisited(flag);
file >> flag;
if (iMap->getRoomType() == roomTypeSecret) iMap->setRevealed(flag);
file >> flag;
iMap->setCleared(flag);
if (iMap->isVisited())
{
for (j = 0; j < MAP_HEIGHT; j++)
{
for (i = 0; i < MAP_WIDTH; i++)
{
file >> n;
iMap->setTile(i, j, n);
file >> n;
iMap->setObjectTile(i, j, n);
file >> n;
iMap->setLogicalTile(i, j, (logicalMapStateEnum)n);
}
}
// style
file >> n;
iMap->setFloorOffset(n);
file >> n;
iMap->setWallType(n);
// items int the map
file >> n;
for (i = 0; i < n; i++)
{
int t;
float x, y;
bool merc;
file >> t >> x >> y >> merc;
iMap->addItem(t, x, y, merc);
}
// chests in the map
file >> n;
for (i = 0; i < n; i++)
{
int t;
float x, y;
bool state;
file >> t >> x >> y >> state;
iMap->addChest(t, state, x, y);
}
// sprites in the map
file >> n;
for (i = 0; i < n; i++)
{
int t, f;
float x, y, scale;
file >> t >> f >> x >> y >> scale;
iMap->addSprite(t, f, x, y, scale);
}
// doors
for (int index = 0; index < 4; index++)
{
file >> n;
iMap->setDoorType(index, (doorEnum)n);
}
// random sprite
for (int index = 0; index < NB_RANDOM_TILES_IN_ROOM; index++)
{
DungeonMap::RandomTileElement rd;
file >> rd.type;
file >> rd.x;
file >> rd.y;
file >> rd.rotation;
iMap->setRandomTileElement(index, rd);
}
// random sprite
}
}
// game
file >> floorX >> floorY;
currentMap = currentFloor->getMap(floorX, floorY);
file >> bossRoomOpened;
// fight ?
file >> saveInFight.isFight;
if (saveInFight.isFight)
{
currentMap->setCleared(false);
file >> saveInFight.x;
file >> saveInFight.y;
file >> saveInFight.direction;
file >> n;
for (i = 0; i < n; i++)
{
StructMonster monster;
file >> j;
monster.id = (enemyTypeEnum)j;
file >> monster.x;
file >> monster.y;
saveInFight.monsters.push_back(monster);
}
}
// player
int hp, hpMax, gold, donation;
file >> hp >> hpMax >> gold >> donation;
player = new PlayerEntity((TILE_WIDTH * MAP_WIDTH * 0.5f),
(TILE_HEIGHT * MAP_HEIGHT * 0.5f));
player->setHp(hp);
player->setHpMax(hpMax);
player->setGold(gold);
player->setDonation(donation);
// score
file >> score;
scoreDisplayed = score;
file >> bodyCount;
// lost hp
for (i = 1; i <= LAST_LEVEL; i++)
{
file >> n;
player->setLostHp(i, n);
}
// equip
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
bool eq;
file >> eq;
player->setEquipped(i, eq);
}
float x, y;
file >> x >> y;
if (saveInFight.isFight)
{
x = saveInFight.x;
y = saveInFight.y;
player->move(saveInFight.direction);
player->setEntering();
}
player->moveTo(x, y);
file >> n;
player->setShotIndex(n);
for (i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
file >> n;
player->setShotType(i, (enumShotType)n);
}
file >> n;
player->setActiveSpell((enumCastSpell)n, saveInFight.isFight);
for (i = 0; i < MAX_SLOT_CONSUMABLES; i++)
{
file >> n;
player->setConsumable(i, n);
}
// divinity
{
int divinityId, piety, divLevel, interventions;
file >> divinityId;
file >> piety;
file >> divLevel;
file >> interventions;
player->loadDivinity(divinityId, piety, divLevel, interventions);
}
// events
for (i = 0; i < NB_EVENTS; i++)
{
bool event;
file >> event;
worldEvent[i] = event;
}
// special states
for (int i = 0; i < NB_SPECIAL_STATES; i++)
{
bool active, waitUnclear;
float timer, param1, param2, param3;
file >> active >> waitUnclear >> timer >> param1 >> param2 >> param3;
player->setSpecialState(enumSpecialState(i), active, timer, param1, param2, waitUnclear);
}
player->computePlayer();
file.close();
- remove(SAVE_FILE.c_str());
+ if (!saveInFight.isFight && !autosave) remove(SAVE_FILE.c_str());
}
else
{
return false;
}
return true;
}
WitchBlastGame::saveHeaderStruct WitchBlastGame::loadGameHeader()
{
saveHeaderStruct saveHeader;
saveHeader.ok = true;
std::ifstream file(SAVE_FILE.c_str(), std::ios::in);
if (file)
{
// version
std::string v;
file >> v;
if (v != SAVE_VERSION)
{
file.close();
remove(SAVE_FILE.c_str());
saveHeader.ok = false;
}
else
{
// date an time
file >> saveHeader.date;
file >> saveHeader.time;
// floor
file >> saveHeader.level;
int challengeLevel;
file >> challengeLevel;
file >> saveHeader.gameTime;
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++)
file >> equipToDisplay[i];
file >> saveHeader.shotType;
}
}
else
{
saveHeader.ok = false;
}
return saveHeader;
}
void WitchBlastGame::saveGameData()
{
- // TODO
std::ofstream file(SAVE_DATA_FILE.c_str(), std::ios::out | std::ios::trunc);
if (file)
{
// version (for compatibility check)
file << SAVE_VERSION << std::endl;
int i;
// tuto
+ file << NB_MESSAGES << std::endl;
for (i = 0; i < NB_MESSAGES; i++)
{
messageStruct msg = getMessage((EnumMessages)i);
+ file << messageEnumToString((EnumMessages)i) << " ";
if (msg.messageType == MessageTypeTutorial)
{
file << gameMessagesToSkip[i] << " ";
}
else file << "0 ";
}
file << std::endl;
// achievements
+ file << NB_ACHIEVEMENTS << std::endl;
for (i = 0; i < NB_ACHIEVEMENTS; i++)
{
+ file << achievementEnumToString((enumAchievementType)i) << " ";
if (achievementState[i] == AchievementDone) file << "1 ";
else file << "0 ";
}
file << std::endl;
// monsters
+ file << NB_ENEMY << std::endl;
for (i = 0; i < NB_ENEMY; i++)
{
- file << globalData.killedMonster[i] << " ";
+ file << enemyString[i] << " " << globalData.killedMonster[i] << " ";
}
file << std::endl;
file.close();
}
else
{
std::cout << "[ERROR] Impossible to open save data file.\n";
}
}
void WitchBlastGame::loadGameData()
{
std::ifstream file(SAVE_DATA_FILE.c_str(), std::ios::in);
int i;
if (file)
{
// version
std::string v;
file >> v;
- if (v != SAVE_VERSION)
+ if (v == "SAVE_0.7")
+ {
+ // tuto
+ for (i = 0; i < NB_MESSAGES; i++)
+ {
+ if (i == MsgInfoDivIllusion)
+ {
+ i += 3;
+ }
+ file >> gameMessagesToSkip[i];
+ }
+
+ // Achievements
+ for (i = 0; i < NB_ACHIEVEMENTS; i++)
+ {
+ int n;
+ file >> n;
+ if (n == 1) achievementState[i] = AchievementDone;
+ else achievementState[i] = AchievementUndone;
+ }
+
+ // Monsters
+ for (i = 0; i < NB_ENEMY; i++)
+ file >> globalData.killedMonster[i];
+ }
+ else if (v != SAVE_VERSION)
{
file.close();
remove(SAVE_DATA_FILE.c_str());
return;
}
-
- // tuto
- for (i = 0; i < NB_MESSAGES; i++)
+ else
{
- file >> gameMessagesToSkip[i];
- }
- }
+ int index, value;
+ std::string key;
- // Achievements
- for (i = 0; i < NB_ACHIEVEMENTS; i++)
- {
- int n;
- file >> n;
- if (n == 1) achievementState[i] = AchievementDone;
- else achievementState[i] = AchievementUndone;
- }
+ // tuto
+ file >> index;
+ for (i = 0; i < index; i++)
+ {
+ file >> key;
+ file >> value;
+ gameMessagesToSkip[messageStringToEnum(key)] = value;
+ }
- // Monsters
- for (i = 0; i < NB_ENEMY; i++)
- file >> globalData.killedMonster[i];
+ // Achievements
+ file >> index;
+ for (i = 0; i < index; i++)
+ {
+ file >> key;
+ file >> value;
+
+ if (value == 1) achievementState[achievementStringToEnum(key)] = AchievementDone;
+ else achievementState[achievementStringToEnum(key)] = AchievementUndone;
+ }
+
+ // Monsters
+ file >> index;
+ for (i = 0; i < index; i++)
+ {
+ file >> key;
+ file >> value;
+ gameMessagesToSkip[messageStringToEnum(key)] = value;
+ globalData.killedMonster[enemyStringToEnum(key)] = value;
+ }
+ }
+ }
}
void WitchBlastGame::addKey(int logicInput, std::string key)
{
int iKey = config.findInt(key);
if (iKey >= 0)
{
sf::Keyboard::Key k = (sf::Keyboard::Key)iKey;
input[logicInput] = k;
}
}
void WitchBlastGame::saveConfigurationToFile()
{
std::map<std::string, std::string> newMap;
// parameters
if (parameters.playerName.length() == 0)
parameters.playerName = "Player";
newMap["language"] = intToString(parameters.language);
newMap["player_name"] = parameters.playerName;
// audio volume
newMap["volume_sound"] = intToString(parameters.soundVolume);
newMap["volume_music"] = intToString(parameters.musicVolume);
newMap["zoom_enabled"] = parameters.zoom ? "1" : "0";
newMap["vsync_enabled"] = parameters.vsync ? "1" : "0";
newMap["blood_spreading"] = parameters.bloodSpread ? "1" : "0";
newMap["fullscreen"] = parameters.fullscreen ? "1" : "0";
newMap["pause_on_focus_lost"] = parameters.pauseOnFocusLost ? "1" : "0";
newMap["particles_batching"] = parameters.particlesBatching ? "1" : "0";
newMap["low_particles"] = parameters.lowParticles ? "1" : "0";
newMap["display_boss_portrait"] = parameters.displayBossPortrait ? "1" : "0";
// Keys
newMap["keyboard_move_up"] = intToString(input[KeyUp]);
newMap["keyboard_move_down"] = intToString(input[KeyDown]);
newMap["keyboard_move_left"] = intToString(input[KeyLeft]);
newMap["keyboard_move_right"] = intToString(input[KeyRight]);
newMap["keyboard_fire_up"] = intToString(input[KeyFireUp]);
newMap["keyboard_fire_down"] = intToString(input[KeyFireDown]);
newMap["keyboard_fire_left"] = intToString(input[KeyFireLeft]);
newMap["keyboard_fire_right"] = intToString(input[KeyFireRight]);
newMap["keyboard_spell"] = intToString(input[KeySpell]);
newMap["keyboard_interact"] = intToString(input[KeyInteract]);
newMap["keyboard_fire"] = intToString(input[KeyFire]);
newMap["keyboard_time_control"] = intToString(input[KeyTimeControl]);
newMap["keyboard_fire_select"] = intToString(input[KeyFireSelect]);
// Joystick
for (unsigned int i = 0; i < NumberKeys; i++)
{
std::stringstream oss_button;
oss_button << "joy_" << inputKeyStr[i] << "_button";
std::stringstream oss_value;
oss_value << "joy_" << inputKeyStr[i] << "_value";
std::stringstream oss_axis;
oss_axis << "joy_" << inputKeyStr[i] << "_axis";
newMap[oss_button.str()] = joystickInput[i].isButton ? "1" : "0";
newMap[oss_value.str()] = intToString(joystickInput[i].value);
newMap[oss_axis.str()] = intToString(joystickInput[i].axis);
}
config.saveToFile(CONFIG_FILE, newMap);
}
void WitchBlastGame::configureFromFile()
{
// default
parameters.language = 0; // english
parameters.zoom = true;
parameters.vsync = true;
parameters.bloodSpread = true;
parameters.fullscreen = false;
parameters.pauseOnFocusLost = true;
parameters.particlesBatching = true;
parameters.lowParticles = false;
parameters.musicVolume = 80;
parameters.soundVolume = 80;
parameters.playerName = "";
parameters.displayBossPortrait = false;
input[KeyUp] = sf::Keyboard::W;
input[KeyDown] = sf::Keyboard::S;
input[KeyLeft] = sf::Keyboard::A;
input[KeyRight] = sf::Keyboard::D;
input[KeyFireUp] = sf::Keyboard::Up;
input[KeyFireDown] = sf::Keyboard::Down;
input[KeyFireLeft] = sf::Keyboard::Left;
input[KeyFireRight] = sf::Keyboard::Right;
input[KeyFire] = sf::Keyboard::RControl;
input[KeySpell] = sf::Keyboard::Space;
input[KeyInteract] = sf::Keyboard::E;
input[KeyFireSelect] = sf::Keyboard::Tab;
input[KeyTimeControl] = sf::Keyboard::RShift;
// Joystick
joystickInput[KeyUp].isButton = false;
joystickInput[KeyUp].axis = sf::Joystick::Y;
joystickInput[KeyUp].value = -1;
joystickInput[KeyDown].isButton = false;
joystickInput[KeyDown].axis = sf::Joystick::Y;
joystickInput[KeyDown].value = 1;
joystickInput[KeyLeft].isButton = false;
joystickInput[KeyLeft].axis = sf::Joystick::X;
joystickInput[KeyLeft].value = -1;
joystickInput[KeyRight].isButton = false;
joystickInput[KeyRight].axis = sf::Joystick::X;
joystickInput[KeyRight].value = 1;
for (unsigned int i = KeyFireUp; i < NumberKeys; i++)
{
joystickInput[i].isButton = true;
joystickInput[i].axis = sf::Joystick::X;
if (i > KeyFireRight) joystickInput[i].value = i - 4;
}
joystickInput[KeyFireUp].value = 3;
joystickInput[KeyFireDown].value = 0;
joystickInput[KeyFireLeft].value = 2;
joystickInput[KeyFireRight].value = 1;
// from file
addKey(KeyUp, "keyboard_move_up");
addKey(KeyDown, "keyboard_move_down");
addKey(KeyLeft, "keyboard_move_left");
addKey(KeyRight, "keyboard_move_right");
addKey(KeyFireUp, "keyboard_fire_up");
addKey(KeyFireDown, "keyboard_fire_down");
addKey(KeyFireLeft, "keyboard_fire_left");
addKey(KeyFireRight, "keyboard_fire_right");
addKey(KeyFire, "keyboard_fire");
addKey(KeySpell, "keyboard_spell");
addKey(KeyInteract, "keyboard_interact");
addKey(KeyTimeControl, "keyboard_time_control");
addKey(KeyFireSelect, "keyboard_fire_select");
// Joystick
for (unsigned int i = 0; i < NumberKeys; i++)
{
std::stringstream oss_button;
oss_button << "joy_" << inputKeyStr[i] << "_button";
std::stringstream oss_value;
oss_value << "joy_" << inputKeyStr[i] << "_value";
std::stringstream oss_axis;
oss_axis << "joy_" << inputKeyStr[i] << "_axis";
int isButton = config.findInt(oss_button.str());
if (isButton > -1000)
joystickInput[i].isButton = isButton;
int n = config.findInt(oss_value.str());
if (n > -1000)
joystickInput[i].value = n;
n = config.findInt(oss_axis.str());
if (n >= sf::Joystick::Axis::X && n <= sf::Joystick::Axis::PovY)
joystickInput[i].axis = (sf::Joystick::Axis)n;
}
int i = config.findInt("language");
if (i >= 0 && i < NB_LANGUAGES) parameters.language = i;
i = config.findInt("volume_sound");
if (i >= 0) parameters.soundVolume = i;
i = config.findInt("volume_music");
if (i >= 0) parameters.musicVolume = i;
i = config.findInt("vsync_enabled");
if (i >= 0) parameters.vsync = i;
i = config.findInt("zoom_enabled");
if (i >= 0) parameters.zoom = i;
i = config.findInt("blood_spreading");
if (i >= 0) parameters.bloodSpread = i;
i = config.findInt("fullscreen");
if (i >= 0) parameters.fullscreen = i;
i = config.findInt("pause_on_focus_lost");
if (i >= 0) parameters.pauseOnFocusLost = i;
i = config.findInt("particles_batching");
if (i >= 0) parameters.particlesBatching = i;
i = config.findInt("low_particles");
if (i >= 0) parameters.lowParticles = i;
i = config.findInt("display_boss_portrait");
if (i >= 0) parameters.displayBossPortrait = i;
std::string playerName = config.findString("player_name");
if (playerName.size() > 0) parameters.playerName = playerName;
tools::setLanguage(languageString[parameters.language]);
}
parameterStruct WitchBlastGame::getParameters()
{
return parameters;
}
void WitchBlastGame::buildMenu(bool rebuild)
{
menuMain.items.clear();
menuConfig.items.clear();
menuFirst.items.clear();
if (!rebuild)
{
menuState = MenuStateMain;
menuMain.age = 0.0f;
menuConfig.age = 0.0f;
menuConfig.index = 0;
}
saveHeader = loadGameHeader();
menuItemStuct itemName;
itemName.label = tools::getLabel("player_name");
itemName.description = tools::getLabel("player_name_desc");
itemName.id = MenuPlayerName;
menuMain.items.push_back(itemName);
if (saveHeader.ok)
{
menuItemStuct itemStart;
std::ostringstream oss;
oss << tools::getLabel("start_new_game");
if (nbPlayers == 1) oss << " (1 " << tools::getLabel("player") << ")";
else oss << " (" << nbPlayers << " " << tools::getLabel("players") << ")";
itemStart.label = oss.str();
itemStart.description = tools::getLabel("start_desc");
itemStart.id = MenuStartNew;
menuMain.items.push_back(itemStart);
menuItemStuct itemLoad;
itemStart.label = tools::getLabel("restore");
oss.str("");
oss << saveHeader.date << " " << tools::getLabel("at") << " " << saveHeader.time << " - " << tools::getLabel("level") << " " << saveHeader.level;
itemStart.description = oss.str();
itemStart.id = MenuStartOld;
menuMain.items.push_back(itemStart);
if (!rebuild) menuMain.index = 2;
}
else
{
menuItemStuct itemStart;
std::ostringstream oss;
oss << tools::getLabel("start_new_game");
if (nbPlayers == 1) oss << " (1 " << tools::getLabel("player") << ")";
else oss << " (" << nbPlayers << " " << tools::getLabel("players") << ")";
itemStart.label = oss.str();
itemStart.description = tools::getLabel("begin_journey");
itemStart.id = MenuStartNew;
menuMain.items.push_back(itemStart);
if (!rebuild) menuMain.index = 1;
}
if (parameters.playerName.compare("") == 0 && menuState == MenuStateMain)
{
menuMain.index = 0;
menuState = MenuStateChangeName;
}
menuItemStuct itemConfig;
itemConfig.label = tools::getLabel("configure_game");
itemConfig.description = tools::getLabel("configure_game_desc");
itemConfig.id = MenuConfig;
menuMain.items.push_back(itemConfig);
if (!isFunctionalityLocked(FunctionalityAchievementsScreen))
{
menuItemStuct itemAchiev;
std::ostringstream oss;
oss << tools::getLabel("menu_achievements");
oss << " (" << getAchievementsPercents() << "%)";
itemAchiev.label = oss.str();
itemAchiev.description = tools::getLabel("menu_achievements_desc");
itemAchiev.id = MenuAchievements;
menuMain.items.push_back(itemAchiev);
}
//if (scores.size() > 0)
{
menuItemStuct itemHiScores;
itemHiScores.label = tools::getLabel("hi_scores");
itemHiScores.description = tools::getLabel("hi_scores_desc");
itemHiScores.id = MenuHiScores;
menuMain.items.push_back(itemHiScores);
}
menuItemStuct itemCredits;
itemCredits.label = tools::getLabel("credits");
itemCredits.description = tools::getLabel("credits_desc");
itemCredits.id = MenuCredits;
menuMain.items.push_back(itemCredits);
menuItemStuct itemExit;
itemExit.label = tools::getLabel("exit_game");
itemExit.description = tools::getLabel("return_to_desktop");
itemExit.id = MenuExit;
menuMain.items.push_back(itemExit);
// configuration
menuItemStuct itemKeys;
itemKeys.label = tools::getLabel("config_keys");
itemKeys.description = tools::getLabel("redef_input");
itemKeys.id = MenuKeys;
menuConfig.items.push_back(itemKeys);
menuItemStuct itemJoystick;
itemJoystick.label = tools::getLabel("config_joystick");
if (sf::Joystick::isConnected(0))
itemJoystick.description = tools::getLabel("redef_joystick");
else
itemJoystick.description = tools::getLabel("joystick_not_found");
itemJoystick.id = MenuJoystick;
menuConfig.items.push_back(itemJoystick);
menuItemStuct itemLanguage;
itemLanguage.label = tools::getLabel("config_lang");
itemLanguage.description = tools::getLabel("config_lang_desc");
itemLanguage.id = MenuLanguage;
menuConfig.items.push_back(itemLanguage);
menuItemStuct itemVolumeSound;
itemVolumeSound.label = tools::getLabel("volume_sound");
itemVolumeSound.description = tools::getLabel("volume_sound_desc");
itemVolumeSound.id = MenuVolumeSound;
menuConfig.items.push_back(itemVolumeSound);
menuItemStuct itemVolumeMusic;
itemVolumeMusic.label = tools::getLabel("volume_music");
itemVolumeMusic.description = tools::getLabel("volume_sound_desc");
itemVolumeMusic.id = MenuVolumeMusic;
menuConfig.items.push_back(itemVolumeMusic);
menuItemStuct itemTutoReset;
itemTutoReset.label = tools::getLabel("tuto_reset");
itemTutoReset.description = tools::getLabel("tuto_reset_desc");
itemTutoReset.id = MenuTutoReset;
menuConfig.items.push_back(itemTutoReset);
menuItemStuct itemConfigBack;
itemConfigBack.label = tools::getLabel("config_back");
itemConfigBack.description = tools::getLabel("config_back_desc");
itemConfigBack.id = MenuConfigBack;
menuConfig.items.push_back(itemConfigBack);
//first time screen
menuFirst.items.push_back(itemLanguage);
}
void WitchBlastGame::buildInGameMenu()
{
menuInGame.items.clear();
menuItemStuct itemContinue;
itemContinue.label = tools::getLabel("menu_continue");
itemContinue.description = tools::getLabel("menu_continue_desc");
itemContinue.id = MenuContinue;
menuInGame.items.push_back(itemContinue);
menuItemStuct itemSaveQuit;
itemSaveQuit.label = tools::getLabel("menu_save_quit");
itemSaveQuit.description = tools::getLabel("menu_save_quit_desc");
itemSaveQuit.id = MenuSaveAndQuit;
menuInGame.items.push_back(itemSaveQuit);
menuItemStuct itemQuit;
itemQuit.label = tools::getLabel("menu_quit");
itemQuit.description = tools::getLabel("menu_quit_desc");
itemQuit.id = MenuExit;
menuInGame.items.push_back(itemQuit);
menuInGame.index = 0;
}
void WitchBlastGame::resetKilledEnemies()
{
for (int i = 0; i < NB_ENEMY; i++) killedEnemies[i] = 0;
}
void WitchBlastGame::addKilledEnemy(enemyTypeEnum enemyType, enumShotType hurtingType)
{
if(!player->isDead())
{
if (enemyType == NB_ENEMY)
std::cout << "[ERROR] No enemy type";
else
{
killedEnemies[enemyType]++;
globalData.killedMonster[enemyType]++;
player->offerMonster(enemyType, hurtingType);
score += getMonsterScore(enemyType);
if (enemyType != EnemyTypeDestroyable) bodyCount++;
// achievements
if (enemyType == EnemyTypeButcher) registerAchievement(AchievementButcher);
else if (enemyType == EnemyTypeSlimeBoss) registerAchievement(AchievementGiantSlime);
else if (enemyType == EnemyTypeCyclops) registerAchievement(AchievementCyclops);
else if (enemyType == EnemyTypeRatKing) registerAchievement(AchievementRatKing);
else if (enemyType == EnemyTypeSpiderGiant) registerAchievement(AchievementGiantSpider);
else if (enemyType == EnemyTypeFranckyHead) registerAchievement(AchievementFrancky);
else if (enemyType == EnemyTypeVampire) registerAchievement(AchievementVampire);
else if ((enemyType == EnemyTypeRat || enemyType == EnemyTypeRatHelmet || enemyType == EnemyTypeRat_invocated
|| enemyType == EnemyTypeRatBlack || enemyType == EnemyTypeRatBlackHelmet))
{
if (globalData.killedMonster[EnemyTypeRat] + globalData.killedMonster[EnemyTypeRatHelmet] + globalData.killedMonster[EnemyTypeRat_invocated]
+ globalData.killedMonster[EnemyTypeRatBlack] + globalData.killedMonster[EnemyTypeRatBlackHelmet] >= 250)
registerAchievement(AchievementRats);
}
else if (enemyType == EnemyTypeWitch || enemyType == EnemyTypeWitchRed)
{
if (globalData.killedMonster[EnemyTypeWitch] + globalData.killedMonster[EnemyTypeWitchRed] >= 50)
registerAchievement(AchievementWitches);
}
else if (enemyType == EnemyTypeGhost)
{
if (globalData.killedMonster[EnemyTypeGhost] >= 50)
registerAchievement(AchievementGhostbuster);
}
else if (enemyType == EnemyTypeSpiderEgg || enemyType == EnemyTypeSpiderEgg_invocated)
{
if (globalData.killedMonster[EnemyTypeSpiderEgg] + globalData.killedMonster[EnemyTypeSpiderEgg_invocated] >= 100)
registerAchievement(AchievementEggs);
}
}
}
}
void WitchBlastGame::registerLanguage()
{
// default keys
if (parameters.language == 1)
{
// french keyboard
input[KeyUp] = sf::Keyboard::Z;
input[KeyLeft] = sf::Keyboard::Q;
}
else
{
// QWERT / QWERTZ keyboard
input[KeyUp] = sf::Keyboard::W;
input[KeyLeft] = sf::Keyboard::A;
}
input[KeyDown] = sf::Keyboard::S;
input[KeyRight] = sf::Keyboard::D;
input[KeyFireUp] = sf::Keyboard::Up;
input[KeyFireDown] = sf::Keyboard::Down;
input[KeyFireLeft] = sf::Keyboard::Left;
input[KeyFireRight] = sf::Keyboard::Right;
input[KeyFire] = sf::Keyboard::RControl;
input[KeyFireSelect] = sf::Keyboard::Tab;
input[KeyTimeControl] = sf::Keyboard::RShift;
input[KeySpell] = sf::Keyboard::Space;
}
int WitchBlastGame::getAchievementsPercents()
{
int n = 0;
for (int i = 0; i < NB_ACHIEVEMENTS; i++)
if (achievementState[i] == AchievementDone) n++;
return (n * 100) / NB_ACHIEVEMENTS;
}
void WitchBlastGame::testAndAddMessageToQueue(EnumMessages type)
{
if (gameMessagesToSkip[type] == false)
{
if (messagesQueue.empty()) SoundManager::getInstance().playSound(SOUND_MESSAGE);
messageStruct msg = getMessage(type);
if (type == MsgInfoDivGift || type == MsgInfoDivIntervention)
{
std::stringstream ss;
ss << tools::getLabel(divinityLabel[player->getDivinity().divinity] + "_0");
ss << " ";
if (type == MsgInfoDivGift)
ss << tools::getLabel("divinity_gift_1");
else // (type == MsgInfoDivIntervention)
ss << tools::getLabel("divinity_intervention_1");
msg.message[1] = ss.str();
}
messagesQueue.push(msg);
if (msg.messageType == MessageTypeTutorial)
{
gameMessagesToSkip[type] = true;
saveGameData();
}
}
}
void WitchBlastGame::addDivLevelMessageToQueue(std::string label)
{
if (label.compare("") == 0) return;
if (messagesQueue.empty()) SoundManager::getInstance().playSound(SOUND_MESSAGE);
messageStruct msg = getMessage(MsgInfoDivLevel);
std::stringstream ss;
ss << tools::getLabel(divinityLabel[player->getDivinity().divinity] + "_0");
ss << " ";
ss << tools::getLabel(label);
msg.message[1] = ss.str();
messagesQueue.push(msg);
}
void WitchBlastGame::initEvents()
{
for (int i = 0; i < NB_EVENTS; i++)
worldEvent[i] = false;
}
void WitchBlastGame::proceedEvent(EnumWorldEvents event)
{
// achievements
if (event == EventGetCoin)
{
if (achievementState[Achievement100] == AchievementUndone && player->getGold() > 100)
registerAchievement(Achievement100);
}
else if (event == EventPietyMax)
{
if (achievementState[AchievementPietyMax] == AchievementUndone) registerAchievement(AchievementPietyMax);
}
else if (event == Event4Hits)
{
if (achievementState[Achievement4Hits] == AchievementUndone) registerAchievement(Achievement4Hits);
}
// message ?
if (worldEvent[event]) return;
worldEvent[event] = true;
testAndAddMessageToQueue(eventToMessage[event]);
}
void WitchBlastGame::renderPlayer(float x, float y,
bool equip[NUMBER_EQUIP_ITEMS], int shotType,
int frame, int spriteDy)
{
bool isMirroring = false;
sf::Sprite sprite;
if (equip[EQUIP_FAIRY_FIRE])
{
sprite.setPosition(x - 35, y - 25);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 144, 48, 60));
app->draw(sprite);
}
if (equip[EQUIP_FAIRY_ICE])
{
sprite.setPosition(x - 5, y - 25);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 216, 48, 60));
app->draw(sprite);
}
if (equip[EQUIP_FAIRY])
{
sprite.setPosition(x - 40, y);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 0, 48, 60));
app->draw(sprite);
}
if (equip[EQUIP_FAIRY_TARGET])
{
sprite.setPosition(x - 10, y);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 72, 48, 60));
app->draw(sprite);
}
if (equip[EQUIP_FAIRY_POISON])
{
sprite.setPosition(x + 20, y);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 288, 48, 60));
app->draw(sprite);
}
if (equip[EQUIP_FAIRY_STONE])
{
sprite.setPosition(x - 5, y + 15);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
sprite.setTextureRect(sf::IntRect( 0, 360, 48, 60));
app->draw(sprite);
}
// slime
if (equip[EQUIP_PET_SLIME])
{
sprite.setPosition(x - 20, y + 24);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_SLIME));
sprite.setTextureRect(sf::IntRect( 64, 256, 64, 64));
app->draw(sprite);
}
sprite.setPosition(x, y);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
int width = 64;
int height = 96;
// body
if (isMirroring)
sprite.setTextureRect(sf::IntRect( frame * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( frame * width, spriteDy * height, width, height));
app->draw(sprite);
// boots
if (equip[EQUIP_BOOTS_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_LEATHER_BOOTS])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_ROBE_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_ROBE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_GLOVES_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_DISPLACEMENT_GLOVES])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_CRITICAL_ADVANCED])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
else if (equip[EQUIP_CRITICAL])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
if (equip[EQUIP_RAGE_AMULET])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
else if (equip[EQUIP_AMULET_RETALIATION])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_2));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (3 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (3 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
if (equip[EQUIP_BELT_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (27 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (27 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_LEATHER_BELT])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (15 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (15 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
// hat
if (equip[EQUIP_HAT_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_HAT])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (6 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (6 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// staff
int frameDx = equip[EQUIP_MAHOGANY_STAFF] ? 6 : 3;
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
if (equip[EQUIP_BLOOD_SNAKE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (27 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (27 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_REAR_SHOT_ADVANCED])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_2));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_REAR_SHOT])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// shot type
if (shotType != ShotTypeStandard)
{
switch (shotType)
{
case ShotTypeIce:
sprite.setColor(sf::Color(100, 220, 255, 255));
break;
case ShotTypeStone:
sprite.setColor(sf::Color(120, 120, 150, 255));
break;
case ShotTypeLightning:
sprite.setColor(sf::Color(255, 255, 0, 255));
break;
case ShotTypeIllusion:
sprite.setColor(sf::Color(240, 180, 250, 255));
break;
case ShotTypeStandard:
sprite.setColor(sf::Color(255, 255, 255, 0));
break;
case ShotTypeFire:
sprite.setColor(sf::Color(255, 180, 0, 255));
break;
default:
break;
}
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (3 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (3 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
}
std::string WitchBlastGame::enemyToString(enemyTypeEnum enemyType)
{
std::string value = "unknown";
switch (enemyType)
{
case EnemyTypeBat_invocated:
case EnemyTypeBat:
value = "enemy_type_bat";
break;
case EnemyTypeBatSkeleton_invocated:
case EnemyTypeBatSkeleton:
value = "enemy_type_bat_skeleton";
break;
case EnemyTypeRat_invocated:
case EnemyTypeRat:
value = "enemy_type_rat";
break;
case EnemyTypeRatBlack:
value = "enemy_type_rat_black";
break;
case EnemyTypeRatHelmet_invocated:
case EnemyTypeRatHelmet:
value = "enemy_type_rat_helmet";
break;
case EnemyTypeRatBlackHelmet:
value = "enemy_type_rat_black_helmet";
break;
case EnemyTypeEvilFlower:
value = "enemy_type_evil_flower";
break;
case EnemyTypeEvilFlowerIce:
value = "enemy_type_evil_flower_ice";
break;
case EnemyTypeEvilFlowerFire:
value = "enemy_type_evil_flower_fire";
break;
case EnemyTypeSnake_invocated:
case EnemyTypeSnake:
value = "enemy_type_snake";
break;
case EnemyTypeSnakeBlood_invocated:
case EnemyTypeSnakeBlood:
value = "enemy_type_snake_blood";
break;
case EnemyTypeSlime_invocated:
case EnemyTypeSlime:
value = "enemy_type_slime";
break;
case EnemyTypeSlimeRed_invocated:
case EnemyTypeSlimeRed:
value = "enemy_type_slime_red";
break;
case EnemyTypeSlimeBlue_invocated:
case EnemyTypeSlimeBlue:
value = "enemy_type_slime_blue";
break;
case EnemyTypeSlimeViolet_invocated:
case EnemyTypeSlimeViolet:
value = "enemy_type_slime_violet";
break;
case EnemyTypeImpBlue:
value = "enemy_type_imp_blue";
break;
case EnemyTypeImpRed:
value = "enemy_type_imp_red";
break;
case EnemyTypePumpkin_invocated:
case EnemyTypePumpkin:
value = "enemy_type_pumpkin";
break;
case EnemyTypeWitch:
value = "enemy_type_witch";
break;
case EnemyTypeWitchRed:
value = "enemy_type_witch_red";
break;
case EnemyTypeCauldron:
value = "enemy_type_cauldron";
break;
case EnemyTypeCauldronElemental:
value = "enemy_type_cauldron_elemental";
break;
case EnemyTypeGhost:
value = "enemy_type_ghost";
break;
case EnemyTypeZombie_invocated:
case EnemyTypeZombie:
value = "enemy_type_zombie";
break;
case EnemyTypeZombieDark:
value = "enemy_type_zombie_dark";
break;
case EnemyTypeBogeyman:
value = "enemy_type_bogeyman";
break;
case EnemyTypeSausage_invocated:
case EnemyTypeSausage:
value = "enemy_type_sausage";
break;
// mini boss
case EnemyTypeBubble:
value = "enemy_type_bubble";
break;
case EnemyTypeBubbleIce:
value = "enemy_type_bubble_ice";
break;
case EnemyTypeBubbleGreater:
value = "enemy_type_bubble_greater";
break;
case EnemyTypeSlimeLarge:
value = "enemy_type_slime_large";
break;
case EnemyTypeSlimeBlueLarge:
value = "enemy_type_slime_blue_large";
break;
case EnemyTypeSlimeRedLarge:
value = "enemy_type_slime_red_large";
break;
case EnemyTypeSlimeVioletLarge:
value = "enemy_type_slime_violet_large";
break;
// boss
case EnemyTypeButcher:
value = "enemy_type_boss_butcher";
break;
case EnemyTypeSlimeBoss:
value = "enemy_type_boss_slime_giant";
break;
case EnemyTypeCyclops:
value = "enemy_type_boss_cyclops";
break;
case EnemyTypeRatKing:
value = "enemy_type_boss_rat_king";
break;
case EnemyTypeSpiderGiant:
value = "enemy_type_boss_spider_giant";
break;
case EnemyTypeFranckyHand:
case EnemyTypeFranckyHead:
case EnemyTypeFranckyFoot:
case EnemyTypeFrancky:
value = "enemy_type_francky";
break;
case EnemyTypeVampire:
case EnemyTypeVampireDead:
value = "enemy_type_vampire";
break;
// invocated
case EnemyTypeRatGreen:
value = "enemy_type_green_rat";
break;
case EnemyTypeRockFalling:
value = "enemy_type_rock_falling";
break;
case EnemyTypeRockMissile:
value = "enemy_type_rock_missile";
break;
case EnemyTypeSpiderEgg:
case EnemyTypeSpiderEgg_invocated:
value = "enemy_type_spider_egg";
break;
case EnemyTypeSpiderLittle:
case EnemyTypeSpiderLittle_invocated:
value = "enemy_type_spider_little";
break;
case EnemyTypeSpiderTarantula:
case EnemyTypeSpiderTarantula_invocated:
value = "enemy_type_spider_tarantula";
break;
case EnemyTypeSpiderWeb:
value = "enemy_type_spider_web";
break;
case EnemyTypeNone:
value = "enemy_type_himself";
break;
case EnemyTypeDestroyable:
value = "";
break;
case NB_ENEMY:
break;
}
return value;
}
std::string WitchBlastGame::sourceToString(sourceTypeEnum sourceType, enemyTypeEnum enemyType)
{
std::string value = "unknown";
switch (sourceType)
{
case SourceTypeBolt:
case SourceTypeMelee:
value = tools::getLabel(enemyToString(enemyType));
break;
case SourceTypeExplosion:
value = tools::getLabel("source_explosion");
break;
case SourceTypePoison:
value = tools::getLabel("source_poison");
}
return value;
}
std::string WitchBlastGame::equipToString(bool equip[NUMBER_EQUIP_ITEMS])
{
std::stringstream oss;
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++)
oss << (equip[i] ? 1 : 0);
return oss.str();
}
void WitchBlastGame::saveHiScores()
{
std::ofstream file(HISCORES_FILE.c_str(), std::ios::out | std::ios::trunc);
if (file)
{
// version (for compatibility check)
file << SAVE_VERSION << std::endl;
file << scores.size() << std::endl;
// tuto
for (unsigned int i = 0; i < scores.size() && i < SCORES_MAX; i++)
{
file << scores[i].name << " ";
file << scores[i].level << " ";
file << scores[i].score << " ";
file << scores[i].shotType << " ";
file << scores[i].divinity << " ";
file << scores[i].killedBy << " ";
file << scores[i].time << " ";
// player equip
for (int j = 0; j < NUMBER_EQUIP_ITEMS; j++)
file << scores[i].equip[j] << " ";
file << std::endl;
}
file << std::endl;
file.close();
}
else
{
std::cout << "[ERROR] Impossible to open hi-scores file.\n";
}
}
void WitchBlastGame::loadHiScores()
{
scores.clear();
std::ifstream file(HISCORES_FILE.c_str(), std::ios::in);
if (file)
{
// version
std::string v;
file >> v;
if (v != SAVE_VERSION)
{
file.close();
- remove(SAVE_DATA_FILE.c_str());
+ remove(HISCORES_FILE.c_str());
return;
}
int nbScores;
file >> nbScores;
for (int i = 0; i < nbScores; i++)
{
StructScore score;
file >> score.name;
file >> score.level;
file >> score.score;
file >> score.shotType;
file >> score.divinity;
file >> score.killedBy;
file >> score.time;
for (int j = 0; j < NUMBER_EQUIP_ITEMS; j++)
file >> score.equip[j];
scores.push_back(score);
}
}
}
void WitchBlastGame::revealFloor()
{
currentFloor->reveal();
refreshMinimap();
}
void WitchBlastGame::activateKeyRoomEffect(bool withColorEffect)
{
dungeonEntity->activateKeyRoomEffect();
if (withColorEffect) makeColorEffect(X_GAME_COLOR_VIOLET, 0.35f);
SoundManager::getInstance().playSound(SOUND_FORCE_FIELD);
}
void WitchBlastGame::generateStar(sf::Color starColor, float xStar, float yStar)
{
SpriteEntity* spriteStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_STAR_2),
xStar, yStar);
spriteStar->setScale(0.8f, 0.8f);
spriteStar->setZ(1000.0f);
spriteStar->setSpin(-100 + rand()%200);
spriteStar->setVelocity(Vector2D(10 + rand()%40));
spriteStar->setWeight(-150);
spriteStar->setFading(true);
spriteStar->setAge(-0.8f);
spriteStar->setLifetime(0.1f + (rand() % 100) * 0.003f );
spriteStar->setColor(starColor);
spriteStar->setType(ENTITY_EFFECT);
}
void WitchBlastGame::registerAchievement(enumAchievementType achievement)
{
if (achievementState[achievement] == AchievementUndone)
{
achievementState[achievement] = AchievementPending;
achievementStruct ach;
ach.type = achievement;
ach.message = tools::getLabel(achievements[achievement].label);
ach.timer = ACHIEVEMENT_DELAY_MAX;
ach.counter = 0;
ach.hasStarted = false;
achievementsQueue.push(ach);
}
}
bool WitchBlastGame::isItemLocked(enumItemType item)
{
for (int i = 0; i < NB_ACHIEVEMENTS; i++)
{
if (achievementState[i] == AchievementUndone)
{
if (achievements[i].unlockType == UnlockItem)
if (achievements[i].unlock == (int)item) return true;
}
}
return false;
}
bool WitchBlastGame::isFunctionalityLocked(enumFunctionalityType func)
{
for (int i = 0; i < NB_ACHIEVEMENTS; i++)
{
if (achievementState[i] == AchievementUndone)
{
if (achievements[i].unlockType == UnlockFunctionality)
if (achievements[i].unlock == (int)func) return true;
}
}
return false;
}
void WitchBlastGame::renderDoors()
{
for (int i = 0; i < 4; i++) doorEntity[i]->renderDoors(app);
}
void WitchBlastGame::resetPresentItems()
{
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++) presentItems[i] = false;
}
bool WitchBlastGame::isPresentItem(int n)
{
if (n >= 0 && n < NUMBER_EQUIP_ITEMS) return presentItems[n];
else return false;
}
void WitchBlastGame::addPresentItem(int n)
{
if (n >= 0 && n < NUMBER_EQUIP_ITEMS) presentItems[n] = true;
}
bool WitchBlastGame::isPressing(int p, inputKeyEnum k, bool oneShot)
{
if (gameState != gameStatePlaying || nbPlayers == 0)
return ((actionKey[0][k].isPressed && (!oneShot || actionKey[0][k].isTriggered))
|| (actionKey[1][k].isPressed && (!oneShot || actionKey[1][k].isTriggered)));
else
return (actionKey[p][k].isPressed && (!oneShot || actionKey[p][k].isTriggered));
}
bool WitchBlastGame::getPressingState(int p, inputKeyEnum k)
{
if (p == 0 || gameState != gameStatePlaying)
{
// arrows in menu
if (gameState != gameStatePlaying
|| player->isDead()
|| player->getPlayerStatus() == PlayerEntity::playerStatusVictorious)
{
if (k == KeyLeft && sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) return true;
if (k == KeyRight && sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) return true;
if (k == KeyUp && sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) return true;
if (k == KeyDown && sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) return true;
if (k == KeyFireDown && sf::Keyboard::isKeyPressed(sf::Keyboard::Return)) return true;
}
// keyboard
if (sf::Keyboard::isKeyPressed(input[k])) return true;
}
if (p == 1 || gameState != gameStatePlaying || nbPlayers == 1)
{
if (!sf::Joystick::isConnected(0)) return false;
// joystick
if (joystickInput[k].isButton)
{
// button
if (joystickInput[k].value >= 0)
if (sf::Joystick::isButtonPressed(0, joystickInput[k].value)) return true;
}
else
{
// axis
if (joystickInput[k].value < 0 && sf::Joystick::getAxisPosition(0, joystickInput[k].axis) < -40) return true;
else if (joystickInput[k].value > 0 && sf::Joystick::getAxisPosition(0, joystickInput[k].axis) > 40) return true;
}
}
return false;
}
void WitchBlastGame::updateActionKeys()
{
for (int p = 0; p < NB_PLAYERS_MAX; p++)
{
for (unsigned int i = 0; i < NumberKeys; i++)
{
bool oldState = actionKey[p][i].isPressed;
actionKey[p][i].isPressed = getPressingState(p, (inputKeyEnum)i);
actionKey[p][i].isTriggered = actionKey[p][i].isPressed && !oldState;
}
}
}
void WitchBlastGame::sendScoreToServer()
{
if (sendScoreThread.joinable()) sendScoreThread.join();
sendScoreThread = std::thread(&WitchBlastGame::sendScoreToServerThread, this);
receiveScoreFromServer();
}
void WitchBlastGame::sendScoreToServerThread()
{
#ifdef ONLINE_MODE
sendScore(lastScore.score,
lastScore.level,
lastScore.name,
equipToString(lastScore.equip),
lastScore.shotType,
lastScore.divinity,
lastScore.killedBy,
lastScore.time,
statsStr,
SCORE_VERSION);
#endif
}
void WitchBlastGame::receiveScoreFromServer()
{
#ifdef ONLINE_MODE
scoreState = ScoreLoading;
if (receiveScoreThread.joinable()) receiveScoreThread.join();
receiveScoreThread = std::thread(&WitchBlastGame::receiveScoreFromServerThread, this);
#endif
}
void WitchBlastGame::receiveScoreFromServerThread()
{
if (sendScoreThread.joinable()) sendScoreThread.join();
loadHiScoresOnline(false);
scoreState = ScoreLoadingDay;
loadHiScoresOnline(true);
scoreState = ScoreOK;
}
void WitchBlastGame::loadHiScoresOnline(bool fromDayOnly)
{
#ifdef ONLINE_MODE
std::vector <StructScore> scoresTemp;
std::vector<std::string> receivedScores = receiveScores(fromDayOnly);
int nbParameters = 8;
if (receivedScores.size() > 0
&& receivedScores.size() % nbParameters == 0
&& receivedScores.size() / nbParameters <= 10)
{
int nbScores = receivedScores.size() / nbParameters;
for (int i = 0; i < nbScores; i++)
{
StructScore score;
std::istringstream scoreStr(receivedScores[i * nbParameters]);
scoreStr >> score.score;
score.name = receivedScores[i * nbParameters + 1];
std::istringstream levelStr(receivedScores[i * nbParameters + 2]);
levelStr >> score.level;
for (int j = 0; j < NUMBER_EQUIP_ITEMS; j++)
{
score.equip[j] = (receivedScores[i * nbParameters + 3])[j] == '1';
}
std::istringstream shotStr(receivedScores[i * nbParameters + 4]);
shotStr >> score.shotType;
std::istringstream divStr(receivedScores[i * nbParameters + 5]);
divStr >> score.divinity;
std::istringstream killStr(receivedScores[i * nbParameters + 6]);
killStr >> score.killedBy;
std::istringstream timeStr(receivedScores[i * nbParameters + 7]);
timeStr >> score.time;
scoresTemp.push_back(score);
}
if (fromDayOnly)
{
scoresOnlineDay.clear();
scoresOnlineDay = scoresTemp;
}
else
{
scoresOnline.clear();
scoresOnline = scoresTemp;
}
}
#endif
}
void WitchBlastGame::checkDestroyableObjects()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
{
if (currentMap->getObjectTile(i, j) >= MAPOBJ_BARREL/* && currentMap->getObjectTile(i, j) < MAPOBJ_BARREL_EXPL + 3*/)
{
new ObstacleEntity(i * TILE_WIDTH + TILE_WIDTH / 2, j * TILE_HEIGHT + TILE_HEIGHT / 2, currentMap->getObjectTile(i, j));
}
}
}
void WitchBlastGame::randomizePotionMap()
{
potionMap.clear();
std::vector<int> potionEffect;
for (int i = 0; i < NUMBER_UNIDENTIFIED; i++) potionEffect.push_back(i);
for (int i = 0; i < NUMBER_UNIDENTIFIED; i++)
{
int r = rand() % potionEffect.size();
addPotionToMap((enumItemType)(ItemPotion01 + i), (enumItemType)(ItemPotionHealth + potionEffect[r]), false);
potionEffect.erase(potionEffect.begin() + r);
}
}
void WitchBlastGame::acquireAlchemyBook()
{
for (int i = 0; i < NUMBER_UNIDENTIFIED; i++)
{
if (rand() % 2 > 0)
{
enumItemType potion = (enumItemType)(ItemPotion01 + i);
if (!potionEffectKnown(potion)) setPotionToKnown(potion);
}
}
// drop potion
ItemEntity* potionItem = new ItemEntity( (enumItemType)(ItemPotion01 + rand() % NUMBER_UNIDENTIFIED), player->getX(), player->getY());
potionItem->setVelocity(Vector2D(100.0f + rand()% 250));
potionItem->setViscosity(0.96f);
potionItem->startsJumping();
}
void WitchBlastGame::addPotionToMap(enumItemType source, enumItemType effect, bool known)
{
potionMap[source] = structPotionMap { effect, known};
potionMap[effect] = structPotionMap { source, known};
}
enumItemType WitchBlastGame::getPotion(enumItemType source)
{
return potionMap[source].effect;
}
bool WitchBlastGame::potionEffectKnown(enumItemType source)
{
return potionMap[source].known;
}
void WitchBlastGame::setPotionToKnown(enumItemType source)
{
potionMap[source].known = true;
potionMap[potionMap[source].effect].known = true;
// message
if (messagesQueue.empty()) SoundManager::getInstance().playSound(SOUND_MESSAGE);
messageStruct msg = getMessage(MsgInfoPotionId);
std::stringstream ss;
ss << msg.message[1];
ss << " ";
ss << tools::getLabel(items[getPotion(source)].name);
msg.message[1] = ss.str();
msg.message[2] = tools::getLabel(items[getPotion(source)].description);
messagesQueue.push(msg);
}
void WitchBlastGame::forgetPotions()
{
for (int i = ItemPotion01; i < FirstEquipItem; i++)
{
potionMap[(enumItemType)i].known = false;
potionMap[potionMap[(enumItemType)i].effect].known = false;
}
}
void WitchBlastGame::forget()
{
// forget map
if (!player->isEquiped(EQUIP_FLOOR_MAP))
{
currentFloor->forget(floorX, floorY);
refreshMinimap();
}
// forget potions
if (!player->isEquiped(EQUIP_BOOK_ALCHEMY))
{
forgetPotions();
}
}
void WitchBlastGame::gainMultiplayerPower()
{
if (nbPlayers == 2)
{
auto fairy = player->getFairy(0);
if (fairy)
{
fairy->gainNewPower();
}
}
}
void WitchBlastGame::addBonusScore(EnumScoreBonus bonusType, int points)
{
if (points <= 0) return;
score += points;
std::ostringstream oss;
std::ostringstream ossStats;
ossStats << ":";
switch (bonusType)
{
case BonusSecret: oss << "Secret!"; ossStats << "S"; break;
case BonusPerfect: oss << "Perfect!"; ossStats << "P"; break;
case BonusChallenge: oss << "Challenge!"; ossStats << "C"; break;
case BonusPossession: oss << "Stuff"; ossStats << "E=" << points; break;
case BonusTime: oss << "Time bonus"; ossStats << "T=" << points; break;
}
statsStr += ossStats.str();
oss << " +" << points;
scoreBonus = oss.str();
scoreBonusTimer = BONUS_TIMER;
SoundManager::getInstance().playSound(SOUND_SCORE_BONUS);
}
void WitchBlastGame::tryToClick(int xMouse, int yMouse, int mouseButton)
{
for (ButtonStruct but : buttons)
{
if (xMouse >= but.zone.left && xMouse <= but.zone.left + but.zone.width
&& yMouse >= but.zone.top && yMouse <= but.zone.top + but.zone.height)
{
if (but.type == ButtonConsumable)
{
if (mouseButton == 0) player->tryToConsume(but.index);
else player->dropConsumables(but.index);
}
else if (but.type == ButtonShotType)
{
player->selectShotType(but.index);
}
return;
}
}
}
WitchBlastGame &game()
{
return *gameptr;
}
diff --git a/src/WitchBlastGame.h b/src/WitchBlastGame.h
index 6ed4715..c732925 100644
--- a/src/WitchBlastGame.h
+++ b/src/WitchBlastGame.h
@@ -1,1171 +1,1176 @@
/** This file is part of Witch Blast.
*
* Witch Blast is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Witch Blast is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WITCH_BLAST_GAME_H
#define WITCH_BLAST_GAME_H
#include "sfml_game/Game.h"
#include "sfml_game/TileMapEntity.h"
#include "PlayerEntity.h"
#include "DungeonMapEntity.h"
#include "MiniMapEntity.h"
#include "EnemyEntity.h"
#include "DoorEntity.h"
#include "GameFloor.h"
#include "Config.h"
#include "Achievements.h"
#include <queue>
#include <thread>
// for tests
//#define TEST_MODE
+// for crashing systems - true enable to continue a game after a crash and send score to DB
+const bool autosave = false;
+
const int ALIGN_LEFT = 0; /*!< Text alignment left */
const int ALIGN_RIGHT = 1; /*!< Text alignment right */
const int ALIGN_CENTER = 2; /*!< Text alignment centered */
const int X_GAME_FADE_IN = 0; /*!< Fade In effect ID */
const int X_GAME_FADE_OUT = 1; /*!< Fade out effect ID */
const int X_GAME_COLOR_RED = 0; /*!< Red light color effect ID */
const int X_GAME_COLOR_GREEN = 1; /*!< Green light color effect ID */
const int X_GAME_COLOR_BLUE = 2; /*!< Blue light color effect ID */
const int X_GAME_COLOR_VIOLET = 3; /*!< Violet light color effect ID */
const int X_GAME_COLOR_BROWN = 4; /*!< Brown light color effect ID */
const int X_GAME_COLOR_WHITE = 5; /*!< White light color effect ID */
unsigned const int NumberKeys = 13; /*!< Number of input keys on the game */
/** Input key string
* Keys in the config file.
*/
const std::string inputKeyString[NumberKeys] =
{
"key_move_up",
"key_move_down",
"key_move_left",
"key_move_right",
"key_fire_up",
"key_fire_down",
"key_fire_left",
"key_fire_right",
"key_fire_select",
"key_spell",
"key_interact",
"key_time",
"key_fire"
};
// TODO: more players, multiple joysticks
const int NB_PLAYERS_MAX = 2;
/** Credits: coder */
const std::string creditsCode[] =
{
"Seby",
"END"
};
/** Credits: 2D artists */
const std::string credits2D[] =
{
"Pierre \"dejam0rt\" Baron",
"END"
};
/** Credits: Sound */
const std::string creditsSound[] =
{
"www.freesound.org/",
"www.freesfx.co.uk/",
"www.universal-soundbank.com/",
"END"
};
/** Credits: Music */
const std::string creditsMusic[] =
{
"Le Guitariste Du Nord",
"END"
};
/** Credits: Translation */
const std::string creditsTranslate[] =
{
"achpile (russian)",
"AFS (spanish)",
"Geheim (german)",
"END"
};
/** Struct for game parameters */
struct parameterStruct
{
int language; /*!< Language ID (english = 0) */
int musicVolume; /*!< music volume (0 to 100) */
int soundVolume; /*!< sound volume (0 to 100) */
bool zoom; /*!< zoom effect (false = disabled) */
bool vsync; /*!< monitor vsync (false = disabled) */
bool bloodSpread; /*!< blood spread (false = disabled) */
bool fullscreen; /*!< full screen (false = disabled) */
bool pauseOnFocusLost; /*!< pause on focus lost (false = disabled) */
bool particlesBatching;
bool lowParticles;
bool displayBossPortrait;
std::string playerName; /*!< player name */
};
struct structPotionMap
{
enumItemType effect;
bool known;
};
struct structStats
{
int hpIni;
int hpMax;
int hpEnd;
int hpMaxEnd;
int hurtCounter;
int healCounter;
int hpLost;
int hpHeal;
int dam;
int goldIni;
};
enum EnumScoreBonus
{
BonusSecret,
BonusChallenge,
BonusPerfect,
BonusTime,
BonusPossession,
};
enum EnumButtonType
{
ButtonConsumable,
ButtonShotType,
};
struct ButtonStruct
{
sf::IntRect zone;
EnumButtonType type;
int index;
};
/*! \class WitchBlastGame
* \brief Main class of the game
*
* WitchBlastGame load the game ressources and do a large part of the game logic (game loop...),
* watches the input, manages the game states, ...
*/
class WitchBlastGame : public Game
{
public:
// DATA TYPES
/** Savegame header data */
struct saveHeaderStruct
{
bool ok; /**< Save game OK ? */
int level; /**< Level the save game */
float gameTime; /**< How long it has been played */
int shotType; /**< Current special shot type */
std::string date; /**< Date of the save game */
std::string time; /**< Time of the save game */
};
// PUBLIC METHODS
/*!
* \brief Constructor
*/
WitchBlastGame();
/*!
* \brief Destructor
*/
virtual ~WitchBlastGame();
/*!
* \brief Accessor on the current dungeon map
* \return a pointer to the current dungeon map
*/
DungeonMap* getCurrentMap();
GameFloor* getCurrentFloor();
int getFloorX();
int getFloorY();
/*!
* \brief Accessor on the current dungeon map entity
* \return a pointer to the current dungeon map entity
*/
DungeonMapEntity* getCurrentMapEntity();
/*!
* \brief Accessor on the player
* \return a pointer to the player
*/
PlayerEntity* getPlayer();
/*!
* \brief Accessor on the player's position
* \return a Vector2D of the player's position
*/
Vector2D getPlayerPosition();
/*!
* \brief accessor on the level
* \return : the level
*/
int getLevel();
/*!
* \brief accessor on the challenge level
* \return : the challenge level
*/
int getChallengeLevel();
/*!
* \brief accessor on the number of found secrets
* \return : the number of found secrets
*/
int getSecretsFound();
/*!
* \brief accessor on showLogical flag
* \return : the value of the flag
*/
bool getShowLogical();
float getDeltaTime();
/*!
* \brief accessor on the parameters
* \return : the parameters
*/
parameterStruct getParameters();
/*!
* \brief Start the game and the game loop
* This method starts the game and the game loop.
* The game loop watches the events, the inputs, update and draw the game elements.
*/
virtual void startGame();
/*!
* \brief Move the player to another map (room)
* Moves the player to another room of the dungeon.
* It's called when a player walk through an opened door.
* \param direction : direction of the new map. 4 = left, 8 = north, 6 = right, 2 = south
*/
void moveToOtherMap(int direction);
/*!
* \brief Closes the doors of the room
* Closes the doors of the room.
* It's called when the player enter in an area with monsters.
*/
void closeDoors();
/*!
* \brief Opens the doors of the room
* Opens the doors of the room.
* It's called when the player destroy the last monster of a room.
*/
void openDoors();
/*!
* \brief Count the enemies in the room
* Count the enemies in the room.
* It's called when the room is closed : 0 enemy = room is cleared and doors shall open.
* \return amount of enemies
*/
/*!
* \brief Refreshes the minimap
*
* Refresh the minimap.
* Called when the player changes room.
*/
void refreshMinimap();
int getEnemyCount();
int getUndeadCount();
+ bool existsEffectZone();
+
void animateEffects();
EnemyEntity* getBoss();
void destroyUndead(int damage);
int getItemsCount();
/*!
* \brief Return the position of the nearest enemy
* \param x : x position of the source
* \param y : y position of the source
* \return position of the nearest enemy (negative position when no enemy found)
*/
Vector2D getNearestEnemy(float x, float y);
/*!
* \brief Generates blood
* \param x : x position of the blood
* \param y : y position of the blood
* \param bloodColor : color of the blood (red; green, ...)
*/
void generateBlood(float x, float y, BaseCreatureEntity::enumBloodColor bloodColor);
/*!
* \brief Add a corpse on the map
* \param x : x position of the corpse
* \param y : y position of the corpse
* \param frame : frame of the corpse in the spritesheet
*/
void addCorpse(float x, float y, int frame);
/*!
* \brief Show a "popup" with artefact's description
* Show a "popup" with artefact's description.
* Artefact's description consists of zoomed picture of the item, name and description.
* It's called when a player get an equipment item.
* \param itemType : item identifier
*/
void showArtefactDescription(enumItemType itemType);
/*!
* \brief Make a "shake" effet
* \param duration : duration of the effect
*/
void makeShake(float duration);
/*!
* \brief Make a "color fade" effet
* \param color : the color of the effect
* \param duration : duration of the effect
*/
void makeColorEffect(int color, float duration);
/*!
* \brief Write a string on screen
* \param str : the string
* \param size : the character size
* \param x : x position
* \param y : y position
* \param align : alignment (ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT)
* \param color : color of the string
* \param app : the rendering target
* \param xShadow : offset of the shadow (x)
* \param yShadow : offset of the shadow (y)
* \param maxWidth : max width of the text image (0 = no limit)
*/
void write(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow = 0, int yShadow = 0, int maxWidth = 0);
void writeGraphic(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow = 0, int yShadow = 0, int maxWidth = 0);
/*!
* \brief Save the game
* Save the game to file : complete floor and maps, items and blood position, player current equipment and stats....
*/
- void saveGame();
+ void saveGame(bool autosave);
/*!
* \brief Load the game
* Load the game from file. After restoring the game, the file is destroy.
* \return true if succeeded
*/
bool loadGame();
/*!
* \brief Save the game data (general)
*/
void saveGameData();
/*!
* \brief Load the game data (general)
*/
void loadGameData();
/*!
* \brief Load the savegame data
* \return the savegame data
*/
saveHeaderStruct loadGameHeader();
/*!
* \brief Returns a random equip object
*
* Returns a random equip object (not an object the player already possesses) .
* \param toSale : true if it's an item for sale
*
* \return the equipment item ID
*/
item_equip_enum getRandomEquipItem(bool toSale, bool noFairy);
/*!
* \brief Returns a random spell object
*/
enumItemType getItemSpell();
/*!
* \brief Generate a random challenge loot
*
* \param x : x position of the bonus
* \param y : y position of the bonus
*/
void generateChallengeBonus(float x, float y);
/*!
* \brief Adds monsters
*
* Adds monsters to the room in suitable places.
* \param monsterType : monster type
* \param amount : amount of monsters
*/
void findPlaceMonsters(enemyTypeEnum monsterType, int amount);
/*!
* \brief Add a monster to the "killed monsters" table
* \param enemyType : ID of the monster
*/
void addKilledEnemy(enemyTypeEnum enemyType, enumShotType damageType);
/*!
* \brief Proceed an event
* \param event : ID of the event
*/
void proceedEvent(EnumWorldEvents event);
/*!
* \brief Proceed a message
* \param event : ID of the message
*/
void testAndAddMessageToQueue(EnumMessages type);
/*!
* \brief Proceed a divinity level up message
* \param label : the message to proceed
*/
void addDivLevelMessageToQueue(std::string label);
std::string enemyToString(enemyTypeEnum enemyType);
std::string sourceToString(sourceTypeEnum sourceType, enemyTypeEnum enemyType);
void saveScreen();
void saveDeathScreen();
struct StructScore
{
std::string name;
int score;
int level;
int shotType;
bool equip[NUMBER_EQUIP_ITEMS];
int divinity;
int killedBy;
int time;
};
void calculateScore();
void addLifeBarToDisplay(std::string label, int hp, int hpMax);
void revealFloor();
void activateKeyRoomEffect(bool withColorEffect);
void generateStar(sf::Color starColor, float xStar, float yStar);
void registerAchievement(enumAchievementType achievement);
void renderDoors();
void setDoorVisible(int n);
bool isPresentItem(int n);
void addPresentItem(int n);
/*!
* \brief Checks if player opens a door
*
* Checks if player opens a door (collide with the door and gets the key).
* If positive, opens the door.
*/
void verifyDoorUnlocking();
float getGameTime();
std::string equipToString(bool equip[NUMBER_EQUIP_ITEMS]);
enumItemType getPotion(enumItemType source);
bool potionEffectKnown(enumItemType source);
void setPotionToKnown(enumItemType source);
void addPotionToMap(enumItemType source, enumItemType effect, bool known);
void acquireAlchemyBook();
void forget();
void forgetPotions();
/*!
* \brief Returns true if the game is in "advanced" mode
*/
bool isAdvanced();
/*!
* \brief Returns true if the difficulty for the map is "advanced"
*/
bool isAdvancedLevel();
void pauseMusic();
void resumeMusic();
void saveStats();
void addHurtingStat(int hpLost);
void addHealingStat(int hpHeal);
void gainMultiplayerPower();
protected:
/*!
* \brief Rendering method
*
* Render all the game objects to the screen.
* Called in the game loop.
*/
virtual void onRender();
/*!
* \brief Update method
*
* Update all the game objects to the screen.
* Called in the game loop.
*/
virtual void onUpdate();
/*!
* \brief render the HUD for shot types
*
* Render the HUD for shot types.
* Display the available shot types and highlight the current one.
*
* \param app : Rendering target
*/
void renderHudShots(sf::RenderTarget* app);
void killArtefactDescription();
private:
Config config;
float deltaTime;
// game logic / data
GameMap* miniMap; /*!< Pointer to the logical minimap */
DungeonMap* currentMap; /*!< Pointer to the logical current map */
GameFloor* currentFloor; /*!< Pointer to the logical floor (level) */
bool showLogical; /*!< True if showing bounding boxes, z and center */
bool showGameTime; /*!< True if showing the game time */
// game play
int level; /*!< Level (floor) */
int score; /*!< score (calculated at the end of the game) */
int scoreDisplayed;
std::string scoreBonus;
float scoreBonusTimer;
int bodyCount; /*!< killed monsters (calculated at the end of the game) */
int challengeLevel; /*!< Level (challenge) */
int secretsFound;
float gameTime; /*!< "age" of the current game */
int floorX; /*!< X position of the room in the level */
int floorY; /*!< Y position of the room in the level */
bool roomClosed; /*!< True if the room is closed */
bool bossRoomOpened; /*!< True if the boss gate has been opened in this level */
int firingDirection; /*!< Save the firing direction - for the "one button" gameplay */
bool isPlayerAlive; /*!< Dying sets this bool to false (trigger the ending music) */
bool monsterArray[MAP_WIDTH][MAP_HEIGHT]; /*!< use to remember if a case has a monster in monster spawn */
int killedEnemies[NB_ENEMY];
int loopCounter;
float endingTimer; /*!< Counter before end of won game */
bool isBonusTimeAdded;
int nbPlayers;
// game objects
PlayerEntity* player; /*!< Pointer to the player entity */
DungeonMapEntity* dungeonEntity; /*!< TileMap of the room (main game board) + blood, items, etc...*/
MiniMapEntity* miniMapEntity;
// displaying objects
DoorEntity* doorEntity[4]; /*!< Pointers to the door graphical entity */
sf::Font font; /*!< The font used for displaying text */
sf::Font graphicsFont; /*!< The font used for displaying "medieval" text */
sf::Text myText; /*!< The text to be displayed */
sf::Sprite introScreenSprite;
sf::Sprite titleSprite;
struct uiSpritesStruct
{
sf::Sprite gui;
sf::Sprite keySprite; /*!< A simple sprite with the boss key (displayed on the HUD) */
sf::Sprite shotsSprite; /*!< A simple sprite for the available shot types (displayed on the HUD) */
sf::Sprite topLayer;
sf::Sprite msgBoxSprite;
sf::Sprite mapBgSprite;
sf::Sprite bagSprite;
sf::Sprite pauseSprite;
sf::Sprite numberSprite;
} uiSprites;
struct lifeBarStruct
{
bool toDisplay;
std::string label;
int hp;
int hpMax;
} lifeBar;
float xOffset, yOffset; /*!< Main game client position in the GUI */
sf::Music music; /*!< Current game music */
/** Music enum
* Identify the various music tracks of the game.
*/
enum musicEnum
{
MusicDungeon, /**< Main game music - played when playing the game */
MusicEnding, /**< Ending music - played when the player has died */
MusicBoss, /**< Boss music - for epic fights ! */
MusicIntro, /**< Main menu music */
MusicChallenge /**< Challenge music - for epic fights ! */
};
/** Game states enum
* Used for the different game states
*/
enum gameStateEnum
{
gameStateInit, /**< Game initialization */
gameStateIntro, /** < Intro animation */
gameStateMenu, /**< Menu */
gameStateKeyConfig, /**< Key config */
gameStateJoystickConfig, /**< Joystick config */
gameStatePlaying, /**< Playing */
gameStatePlayingPause, /**< Playing / Pause */
gameStatePlayingDisplayBoss, /**< Playing / DisplayBoss */
};
gameStateEnum gameState; /*!< Store the game state */
float bossDisplayTimer;
int bossDisplayState;
/** Special game states enum
* Used for effects such as fade in...
*/
enum xGameTypeEnum
{
xGameTypeFade, /**< Fade effect */
xGameTypeShake, /**< Shake effect */
xGameTypeFadeColor, /**< Color fade effect */
NB_X_GAME
};
struct xGameStruct
{
bool active;
int param;
float timer;
float duration;
} xGame[NB_X_GAME];
/** Input Keys enum
* Used for the input binding
*/
enum inputKeyEnum
{
KeyUp,
KeyDown,
KeyLeft,
KeyRight,
KeyFireUp,
KeyFireDown,
KeyFireLeft,
KeyFireRight,
KeyFireSelect,
KeySpell,
KeyInteract,
KeyTimeControl,
KeyFire
};
const std::string inputKeyStr[NumberKeys] =
{
"_move_up",
"_move_down",
"_move_left",
"_move_right",
"_fire_up",
"_fire_down",
"_fire_left",
"_fire_right",
"_spell",
"_interact",
"_fire",
"_time_control",
"_fire_select"
};
sf::Keyboard::Key input[NumberKeys]; /*!< Input key array */
struct JoystickInputStruct
{
bool isButton;
sf::Joystick::Axis axis;
int value;
};
JoystickInputStruct joystickInput[NumberKeys];
std::string scoreSaveFile;
structStats statsData;
std::string statsStr;
/*!
* \brief Starts the game
*
* Start a new game or load it from file.
*
* \param fromSaveFile : true if we want to try to load the game from a file
*/
void startNewGame(bool fromSaveFile, int startingLevel);
/*!
* \brief Starts a new level
*
* Start a new level.
* Called for each level of the game.
*/
void startNewLevel();
/*!
* \brief Starts the level
*
* Starts the level.
* Called after loading the game or creating a new level.
* \param isFight : true if loading in fight
*/
void playLevel(bool isFight);
/*!
* \brief Creates a level
*
* Creates a random level (a level is a floor that consists of rooms).
*/
void createFloor();
/*!
* \brief Create or refresh the room
*
* Create a room (if this is a new one) or refresh it.
* Called when the player changes room.
* Checks the visibility of the doors and close it if there are monsters.
* Loads the map items and sprites.
*/
void refreshMap();
/*!
* \brief Check if a door is present, and its style
*
* \param doorId : index of the door
* \param roomCurrent : type of the current door
* \param roomNeighbour : type of the neighbour door
*/
void checkDoor(int doorId, roomTypeEnum roomCurrent, roomTypeEnum roomNeighbour, bool isNeighbourKnown);
/*!
* \brief Generates a room
*
* Generates a room.
* Called when the player moves to a room for the first time.
*/
void generateMap();
/*!
* \brief Generates a standard room
*
* Generates a standard room with monsters.
* Called during the generation when the map has no particular type.
*/
void generateStandardMap();
/*!
* \brief Checks if the room will be closed
*
* Checks if the room will be closed.
* Called when entering a room. If the room is not clear, closes the doors.
*/
void checkEntering();
/*!
* \brief Saves the map items
*
* Saves the map objects such as items, corpses, blood, chest.
* Called when leaving the door.
*/
void saveMapItems();
/*!
* \brief Initializes the monster array
*
* Initializes the monster array (to empty).
*/
void initMonsterArray();
/*!
* \brief Adds a monster
*
* Adds a monster to the room.
* \param monsterType : monster type
* \param xm : x position of the monster
* \param ym : y position of the monster
*/
void addMonster(enemyTypeEnum monsterType, float xm, float ym);
/*!
* \brief Checks if player can interact with something
*/
void checkInteraction();
/*!
* \brief Plays a music
*
* Plays a music.
*
* \param musicChoice : music track ID
*/
void playMusic(musicEnum musicChoice);
/*!
* \brief Update the music volume (with parameters.volumeMusic)
*/
void updateMusicVolume();
/*!
* \brief Add a key to the player input map from a string key (from file)
* \param logicInput : input function (move left, fire up, etc...)
* \param key : Key as string
*/
void addKey(int logicInput, std::string key);
/*!
* \brief Save configuration to "config.dat"
*/
void saveConfigurationToFile();
/*!
* \brief Configure with data from "config.dat"
*/
void configureFromFile();
/*!
* \brief Update the game
*/
void updateRunningGame();
/*!
* \brief Render the game
*/
void renderRunningGame();
void renderGame();
void renderHud();
void renderLifeBar();
void renderScore();
void renderMessages();
void renderBossPortrait();
/*!
* \brief Update the menu
*/
void updateMenu();
/*!
* \brief Render the menu
*/
void renderMenu();
/*!
* \brief Render the menu
*/
void renderInGameMenu();
/*!
* \brief initialize the intro
*/
void prepareIntro();
/*!
* \brief Update the intro
*/
void updateIntro();
/*!
* \brief Render the intro
*/
void renderIntro();
/*!
* \brief Render the credits screen
*/
void renderCredits();
/*!
* \brief Render the achievements screen
*/
void renderAchievements();
/*!
* \brief Render the scores screen
*/
void renderScores(std::vector <StructScore> scoresToRender, std::string title, bool blinkingName);
/** Menu keys enum
* Identify the various keys of the menu.
*/
enum menuItemEnum
{
MenuStartNew, /**< When starting the game */
MenuStartOld, /**< When restoring the game */
MenuConfig, /**< When configuring the game */
MenuKeys, /**< When configuring keys */
MenuJoystick, /**< When configuring joystick */
MenuConfigBack, /**< Back to the main menu */
MenuTutoReset, /**< Reset the tutorials */
MenuLanguage, /**< When configuring the language */
MenuExit, /**< When exiting the game */
MenuAchievements, /**< Display the Achievements */
MenuCredits, /**< Display the credits screen */
MenuHiScores, /**< Display the hi-scores screen */
MenuPlayerName, /**< To enter/change the player name */
MenuVolumeSound,
MenuVolumeMusic,
MenuContinue, /**< Continue the game */
MenuSaveAndQuit, /**< Save and return to main */
};
/** Menu states enum
* Identify the various states of the menu.
*/
enum menuStateEnum
{
MenuStateMain,
MenuStateConfig,
MenuStateKeys,
MenuStateJoystick,
MenuStateHiScores,
MenuStateChangeName,
MenuStateCredits,
MenuStateAchievements,
MenuStateFirst /**< First time, we choose language and keyboard */
};
menuStateEnum menuState;
/*!
* \brief Menu item structure
*/
struct menuItemStuct
{
menuItemEnum id; /**< Id of the action */
std::string label; /**< Label of the menu item */
std::string description; /**< Description of the menu item */
};
int menuScoreIndex;
/*!
* \brief Menu structure
*/
struct menuStuct
{
std::vector<menuItemStuct> items; /**< Menu items */
unsigned int index; /**< Position int the menu */
float age; /**< Age of the menu */
};
menuStuct menuMain;
menuStuct menuFirst;
menuStuct menuConfig;
menuStuct menuJoystick;
menuStuct menuInGame;
unsigned int menuKeyIndex;
unsigned int menuAchIndex;
/*!
* \brief Build the menu items
*/
void buildMenu(bool rebuild);
/*!
* \brief Build the menu items (in game)
*/
void buildInGameMenu();
/*!
* \brief Switch to the menu
*/
void switchToMenu();
/*!
* \brief Save language config and default keys configuration
*/
void registerLanguage();
parameterStruct parameters;
void resetKilledEnemies();
std::queue <messageStruct> messagesQueue;
bool worldEvent[NB_EVENTS];
void initEvents();
bool gameMessagesToSkip[NB_MESSAGES];
struct achievementStruct
{
enumAchievementType type;
std::string message;
float timer;
bool hasStarted;
int counter;
};
std::queue <achievementStruct> achievementsQueue;
void renderPlayer(float x, float y, bool equip[NUMBER_EQUIP_ITEMS], int shotType,
int frame, int spriteDy);
void renderDeathScreen(float x, float y);
bool equipToDisplay[NUMBER_EQUIP_ITEMS];
bool equipNudeToDisplay[NUMBER_EQUIP_ITEMS];
saveHeaderStruct saveHeader;
struct StructMonster
{
enemyTypeEnum id;
float x;
float y;
};
/*!
* \brief Data for "in fight" game saving
*/
struct StructSaveInFight
{
bool isFight; /**< True if loading during a fight */
float x; /**< Player x position */
float y; /**< Player y position */
int direction; /**< Player direction */
std::vector<StructMonster> monsters; /**< Monsters */
};
StructSaveInFight saveInFight; /**< Data for "in fight" game saving */
SpriteEntity* introSprites[8];
int introState;
int introSoundState;
int fairySpriteOffsetY;
int currentStandardMusic;
std::vector <StructScore> scores;
std::vector <StructScore> scoresOnline;
std::vector <StructScore> scoresOnlineDay;
StructScore lastScore;
void loadHiScores();
void loadHiScoresOnline(bool fromDayOnly);
void saveHiScores();
enum enumNetworkScoreState
{
ScoreOK,
ScoreLoading,
ScoreLoadingDay
} scoreState;
struct StructInteraction
{
bool active;
EnumInteractionType type;
int id;
std::string label;
} interaction;
void enableAA(bool enable);
enum achievementStatus { AchievementDone, AchievementUndone, AchievementPending};
achievementStatus achievementState[NB_ACHIEVEMENTS];
bool isItemLocked(enumItemType item);
bool isFunctionalityLocked(enumFunctionalityType func);
int getAchievementsPercents();
struct globalDataStruct
{
int killedMonster[NB_ENEMY];
} globalData;
void generateUiParticle(float x, float y);
bool presentItems[NUMBER_EQUIP_ITEMS];
void resetPresentItems();
bool isPressing(int p, inputKeyEnum k, bool oneShot);
bool getPressingState(int p, inputKeyEnum k);
void updateActionKeys();
struct ActionKeyStruct
{
bool isPressed;
bool isTriggered;
};
ActionKeyStruct actionKey[NB_PLAYERS_MAX][NumberKeys];
bool isInputPressed[NB_PLAYERS_MAX][NumberKeys];
bool gameFromSaveFile;
// scoring server
std::thread sendScoreThread;
void sendScoreToServer();
void sendScoreToServerThread();
std::thread receiveScoreThread;
void receiveScoreFromServer();
void receiveScoreFromServerThread();
void checkDestroyableObjects();
std::map<enumItemType, structPotionMap> potionMap;
void randomizePotionMap();
void addBonusScore(EnumScoreBonus bonusType, int points);
sf::Vector2i levelStrPosition;
std::vector<ButtonStruct> buttons;
void tryToClick(int xMouse, int yMouse, int mouseButton);
};
/*!
* \brief Returns the game reference
*
* Returns the game reference.
*
* \return a reference to the game
*/
WitchBlastGame& game();
#endif // WITCH_BLAST_GAME_H
diff --git a/src/WitchEntity.cpp b/src/WitchEntity.cpp
index a98954f..bfc530a 100644
--- a/src/WitchEntity.cpp
+++ b/src/WitchEntity.cpp
@@ -1,250 +1,250 @@
#include "WitchEntity.h"
#include "BoltEntity.h"
#include "EnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "RatEntity.h"
#include "BatEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
WitchEntity::WitchEntity(float x, float y, witchTypeEnum witchType)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_WITCH), x, y)
{
this->witchType = witchType;
imagesProLine = 8;
if (witchType == WitchTypeNormal)
{
frame = 0;
dyingFrame = 5;
deathFrame = FRAME_CORPSE_WITCH;
enemyType = EnemyTypeWitch;
}
else
{
frame = 8;
dyingFrame = 13;
deathFrame = FRAME_CORPSE_WITCH_RED;
enemyType = EnemyTypeWitchRed;
}
hp = WITCH_HP;
hpMax = hp;
creatureSpeed = WITCH_VELOCITY;
velocity = Vector2D(creatureSpeed);
meleeDamages = WITCH_DAMAGE;
bloodColor = BloodRed;
shadowFrame = 4;
height = 96;
sprite.setOrigin(32, 71);
timer = 3.0f;
escapeTimer = -1.0f;
state = 0;
agonizingSound = (sound_resources)(SOUND_WITCH_DIE_00 + rand() % 2);
}
void WitchEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
if (escapeTimer > 0.0f) escapeTimer -= delay;
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0)
{
state = 1;
velocity = Vector2D(0.0f, 0.0f);
SoundManager::getInstance().playSound(SOUND_WITCH_00 + rand() % 3);
timer = 0.6f;
if (rand() % 7 == 0 || !canSee(game().getPlayerPosition().x, game().getPlayerPosition().y))
{
// invoke
int x0 = x / TILE_WIDTH;
if (x0 < 1) x0 = 1;
else if(x0 > MAP_WIDTH - 2) x0 = MAP_WIDTH - 2;
x0 = x0 * TILE_WIDTH + TILE_WIDTH / 2;
int y0 = y / TILE_HEIGHT;
if (y0 < 1) y0 = 1;
else if(y0 > MAP_HEIGHT - 2) y0 = MAP_HEIGHT - 2;
y0 = y0 * TILE_HEIGHT + TILE_HEIGHT / 2;
if (witchType == WitchTypeNormal) new RatEntity(x0, y0, RatEntity::RatTypeNormal, true);
else new BatEntity(x0, y0, BatStandard, true);
SoundManager::getInstance().playSound(SOUND_INVOKE);
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(200, 50, 200, 255));
generateStar(sf::Color(255, 255, 255, 255));
}
}
else
{
// fire
fire();
if (witchType == WitchTypeNormal) fire();
}
}
else if (state == 1)
{
timer = 3.5f + (rand() % 25) * 0.1f;
if (!canSee(game().getPlayerPosition().x, game().getPlayerPosition().y)) timer += 1.2f;
velocity = Vector2D(creatureSpeed);
state = 0;
}
}
if (state == 0)
{
if (escapeTimer < 0.0f && Vector2D(x, y).distance2(game().getPlayerPosition()) <= 36000)
{
velocity = game().getPlayerPosition().vectorTo(Vector2D(x, y), creatureSpeed);
escapeTimer = 2.5f;
}
frame = ((int)(age * 5.0f)) % 4;
if (frame == 2) frame = 0;
else if (frame == 3) frame = 2;
if (velocity.x > 1.0f) isMirroring = true;
else if (velocity.x < -1.0f) isMirroring = false;
}
else if (state == 1)
{
frame = 3;
isMirroring = game().getPlayer()->getX() > x;
}
if (witchType == WitchTypeRed) frame += 8;
}
EnemyEntity::animate(delay);
z = y + 20;
}
void WitchEntity::calculateBB()
{
boundingBox.left = (int)x - 16;
boundingBox.width = 32;
boundingBox.top = (int)y - 25;
boundingBox.height = 45;
}
void WitchEntity::collideMapRight()
{
velocity.x = -velocity.x;
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
else computeFacingDirection();
}
void WitchEntity::collideMapLeft()
{
velocity.x = -velocity.x;
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
else computeFacingDirection();
}
void WitchEntity::collideMapTop()
{
velocity.y = -velocity.y;
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
else computeFacingDirection();
}
void WitchEntity::collideMapBottom()
{
velocity.y = -velocity.y;
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
else computeFacingDirection();
}
void WitchEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
setVelocity(Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), creatureSpeed ));
computeFacingDirection();
}
}
void WitchEntity::collideWithBolt(BoltEntity* boltEntity)
{
EnemyEntity::collideWithBolt(boltEntity);
}
void WitchEntity::fire()
{
if (witchType == WitchTypeNormal)
{
SoundManager::getInstance().playSound(SOUND_BLAST_FLOWER);
EnemyBoltEntity* bolt = new EnemyBoltEntity
(x, y + 10, ShotTypeStandard, 0, enemyType);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
float flowerFireVelocity = EVIL_FLOWER_FIRE_VELOCITY;
if (specialState[SpecialStateIce].active) flowerFireVelocity *= 0.7f;
bolt->setVelocity(Vector2D(x, y).vectorNearlyTo(game().getPlayerPosition(), flowerFireVelocity, 1.0f ));
}
else
{
SoundManager::getInstance().playSound(SOUND_BLAST_FLOWER);
EnemyBoltEntity* bolt = new EnemyBoltEntity
(x, y + 10, ShotTypeBomb, 0, enemyType);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
float flowerFireVelocity = EVIL_FLOWER_FIRE_VELOCITY * 0.9f;
if (specialState[SpecialStateIce].active) flowerFireVelocity *= 0.5f;
bolt->setVelocity(Vector2D(x, y).vectorNearlyTo(game().getPlayerPosition(), flowerFireVelocity, 1.0f ));
}
}
void WitchEntity::drop()
{
if (rand() % 12 == 0)
{
if (rand() % 5 == 0)
{
dropItem(ItemScrollRevelation);
}
else
{
dropItem((enumItemType)(ItemPotion01 + rand() % NUMBER_UNIDENTIFIED));
}
return;
}
else
{
if (rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
if (game().getPlayer()->isEquiped(EQUIP_LUCK) && rand() % 5 == 0)
{
dropItem(ItemCopperCoin);
}
}
}
diff --git a/src/ZombieDarkEntity.cpp b/src/ZombieDarkEntity.cpp
index 5ebadf2..96e2e11 100644
--- a/src/ZombieDarkEntity.cpp
+++ b/src/ZombieDarkEntity.cpp
@@ -1,345 +1,345 @@
#include "ZombieDarkEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
ZombieDarkEntity::ZombieDarkEntity(float x, float y)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_ZOMBIE), x, y),
currentTile(0, 0),
targetTile(0, 0)
{
imagesProLine = 11;
frame = 11;
dyingFrame = 21;
bloodColor = BloodRed;
shadowFrame = 9;
deathFrame = FRAME_CORPSE_ZOMBIE_DARK;
enemyType = EnemyTypeZombieDark;
hp = ZOMBIE_HP;
creatureSpeed = ZOMBIE_SPEED;
meleeDamages = ZOMBIE_DAMAGE;
agonizingSound = SOUND_ZOMBIE_DYING;
currentDirection = 2 + 2 * rand()%4;
facingDirection = currentDirection;
nextFacingDirection = currentDirection;
height = 80;
sprite.setOrigin(32.0f, 60.0f);
attackTimer = 0.9f;
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceHigh;
+ resistance[ResistanceRepulsion] = ResistanceHigh;
resistance[ResistancePoison] = ResistanceImmune;
findNextGoal();
}
void ZombieDarkEntity::animate(float delay)
{
if (age > 0.0f && !isAgonising)
{
// goal reached ?
if (currentDirection == 6 && x > (targetTile.x * TILE_WIDTH + TILE_WIDTH / 2) ) findNextGoal();
else if (currentDirection == 4 && x < (targetTile.x * TILE_WIDTH + TILE_WIDTH / 2) ) findNextGoal();
else if (currentDirection == 2 && y > (targetTile.y * TILE_HEIGHT + TILE_HEIGHT / 2 - 5) ) findNextGoal();
else if (currentDirection == 8 && y < (targetTile.y * TILE_HEIGHT + TILE_HEIGHT / 2 - 5) ) findNextGoal();
checkNextFacing(delay);
frame = ((int)(age * 4.0f)) % 4;
if (frame == 3) frame = 1;
if (facingDirection == 4 || facingDirection == 6) frame += 3;
isMirroring = (facingDirection == 4 );
if (facingDirection == 8) frame += 6;
frame += 11;
attackTimer -= delay;
}
EnemyEntity::animate(delay);
z = y + 17;
}
void ZombieDarkEntity::calculateBB()
{
boundingBox.left = (int)x - 14;
boundingBox.width = 28;
boundingBox.top = (int)y - 18;
boundingBox.height = 36;
}
void ZombieDarkEntity::collideMapRight()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
findNextRandomGoal();
}
void ZombieDarkEntity::collideMapLeft()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
findNextRandomGoal();
}
void ZombieDarkEntity::collideMapTop()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
findNextRandomGoal();
}
void ZombieDarkEntity::collideMapBottom()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
findNextRandomGoal();
}
void ZombieDarkEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
if (currentDirection == 6 && entity->getX() > x)
{
currentDirection = 4;
targetTile = IntCoord(currentTile.x - 1, currentTile.y);
}
else if (currentDirection == 4 && entity->getX() < x)
{
currentDirection = 6;
targetTile = IntCoord(currentTile.x + 1, currentTile.y);
}
else if (currentDirection == 8 && entity->getY() < y)
{
currentDirection = 2;
targetTile = IntCoord(currentTile.x, currentTile.y + 1);
}
else if (currentDirection == 2 && entity->getY() > y)
{
currentDirection = 8;
targetTile = IntCoord(currentTile.x, currentTile.y - 1);
}
switch (currentDirection)
{
case 4: velocity.x = - creatureSpeed; velocity.y = 0.0f; break;
case 6: velocity.x = + creatureSpeed; velocity.y = 0.0f; break;
case 2: velocity.y = + creatureSpeed; velocity.x = 0.0f; break;
case 8: velocity.y = - creatureSpeed; velocity.x = 0.0f; break;
default: break;
}
nextFacingDirection = currentDirection;
}
}
void ZombieDarkEntity::findNextGoal()
{
currentTile = getCurrentTile();
float xPlayer = game().getPlayerPosition().x;
float yPlayer = game().getPlayerPosition().y;
float xDist = abs(x - xPlayer);
float yDist = abs(y - yPlayer);
int playerDirecion;
if (xDist >= yDist)
{
if (xPlayer > x) playerDirecion = 6;
else playerDirecion = 4;
}
else
{
if (yPlayer > y) playerDirecion = 2;
else playerDirecion = 8;
}
switch (currentDirection)
{
case 4:
if (playerDirecion != 6)
currentDirection = playerDirecion;
else
{
if (yPlayer > y) currentDirection = 2;
else currentDirection = 8;
}
break;
case 6:
if (playerDirecion != 4)
currentDirection = playerDirecion;
else
{
if (yPlayer > y) currentDirection = 2;
else currentDirection = 8;
}
break;
case 2:
if (playerDirecion != 8)
currentDirection = playerDirecion;
else
{
if (xPlayer > x) currentDirection = 6;
else currentDirection = 4;
}
break;
case 8:
if (playerDirecion != 2)
currentDirection = playerDirecion;
else
{
if (xPlayer > x) currentDirection = 6;
else currentDirection = 4;
}
break;
default: break;
}
switch (currentDirection)
{
case 4:
velocity.x = - creatureSpeed;
velocity.y = 0.0f;
targetTile = IntCoord(currentTile.x - 2, currentTile.y);
break;
case 6:
velocity.x = + creatureSpeed;
velocity.y = 0.0f;
targetTile = IntCoord(currentTile.x + 2, currentTile.y);
break;
case 2:
velocity.y = + creatureSpeed;
velocity.x = 0.0f;
targetTile = IntCoord(currentTile.x, currentTile.y + 2);
break;
case 8:
velocity.y = - creatureSpeed;
velocity.x = 0.0f;
targetTile = IntCoord(currentTile.x, currentTile.y - 2);
break;
default: break;
}
nextFacingDirection = currentDirection;
if (currentDirection == playerDirecion && attackTimer <= 0.0f)
{
- giveRecoil(false, Vector2D(velocity.x * 2.0f, velocity.y * 2.0f), 1.5f);
+ giveRepulsion(false, Vector2D(velocity.x * 2.0f, velocity.y * 2.0f), 1.5f);
attackTimer = 2.0f;
SoundManager::getInstance().playSound(SOUND_ZOMBIE_ATTACKING);
facingTimer = -1.0f;
}
else
SoundManager::getInstance().playSound(SOUND_ZOMBIE_00 + rand() % 2);
}
void ZombieDarkEntity::findNextRandomGoal()
{
currentTile = getCurrentTile();
DungeonMap* dMap = game().getCurrentMap();
int backDirection = 0;
switch (currentDirection)
{
case 4: backDirection = 6; break;
case 6: backDirection = 4; break;
case 2: backDirection = 8; break;
case 8: backDirection = 2; break;
default: break;
}
bool ok = false;
{
int r = 0;
while (!ok)
{
r++;
if (r == 150) // watchdog
ok = true;
else if (r == 40)
{
backDirection = 5;
}
int newDir = rand() % 4;
if (newDir == 0)
{
if (backDirection != 4 && currentTile.x > 1 && (currentTile.y % 2 != 0) && dMap->isWalkable(currentTile.x - 1, currentTile.y))
{
currentDirection = 4;
targetTile = IntCoord(currentTile.x - 1, currentTile.y);
ok = true;
}
}
else if (newDir == 1)
{
if (backDirection != 6 && currentTile.x < MAP_WIDTH - 2 && (currentTile.y % 2 != 0) && dMap->isWalkable(currentTile.x + 1, currentTile.y))
{
currentDirection = 6;
targetTile = IntCoord(currentTile.x + 1, currentTile.y);
ok = true;
}
}
else if (newDir == 2)
{
if (backDirection != 8 && currentTile.y > 1 && (currentTile.x % 2 != 0) && dMap->isWalkable(currentTile.x, currentTile.y - 1))
{
currentDirection = 8;
targetTile = IntCoord(currentTile.x, currentTile.y - 1);
ok = true;
}
}
else
{
if (backDirection != 2 && currentTile.y < MAP_HEIGHT - 2 && (currentTile.x % 2 != 0) && dMap->isWalkable(currentTile.x, currentTile.y + 1))
{
currentDirection = 2;
targetTile = IntCoord(currentTile.x, currentTile.y + 1);
ok = true;
}
}
}
}
switch (currentDirection)
{
case 4: velocity.x = - creatureSpeed; velocity.y = 0.0f; break;
case 6: velocity.x = + creatureSpeed; velocity.y = 0.0f; break;
case 2: velocity.y = + creatureSpeed; velocity.x = 0.0f; break;
case 8: velocity.y = - creatureSpeed; velocity.x = 0.0f; break;
default: break;
}
nextFacingDirection = currentDirection;
}
void ZombieDarkEntity::collideWithBolt(BoltEntity* boltEntity)
{
EnemyEntity::collideWithBolt(boltEntity);
}
bool ZombieDarkEntity::isAttacking()
{
return attackTimer > 1.0f;
}
diff --git a/src/ZombieEntity.cpp b/src/ZombieEntity.cpp
index 65f58dc..d283f8a 100644
--- a/src/ZombieEntity.cpp
+++ b/src/ZombieEntity.cpp
@@ -1,262 +1,262 @@
#include "ZombieEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
ZombieEntity::ZombieEntity(float x, float y, bool invocated)
: EnemyEntity (ImageManager::getInstance().getImage(IMAGE_ZOMBIE), x, y)
{
imagesProLine = 11;
this->invocated = invocated;
frame = 1;
dyingFrame = 10;
bloodColor = BloodRed;
shadowFrame = 9;
deathFrame = FRAME_CORPSE_ZOMBIE;
if (invocated) enemyType = EnemyTypeZombie_invocated;
else enemyType = EnemyTypeZombie;
hp = ZOMBIE_HP;
creatureSpeed = ZOMBIE_SPEED;
direction = rand() % 4;
facingDirection = direction;
nextFacingDirection = direction;
clockTurn = rand() % 2 == 0;
compute(false);
timer = 5 + rand() % 6;
attackTimer = 0.9f;
meleeDamages = ZOMBIE_DAMAGE;
agonizingSound = SOUND_ZOMBIE_DYING;
height = 80;
sprite.setOrigin(32.0f, 60.0f);
resistance[ResistanceFrozen] = ResistanceHigh;
- resistance[ResistanceRecoil] = ResistanceHigh;
+ resistance[ResistanceRepulsion] = ResistanceHigh;
resistance[ResistancePoison] = ResistanceImmune;
}
void ZombieEntity::animate(float delay)
{
if (isAgonising)
{
if (hpDisplay > hp) hpDisplay--;
if (h < -0.01f)
{
isAgonising = false;
isDying = true;
game().addCorpse(x, y, deathFrame);
if (dyingSound != SOUND_NONE) SoundManager::getInstance().playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
else if (age > 0.0f)
{
if (attackTimer <= 0.0f && attack())
{
attackTimer = 2.0f;
- giveRecoil(false, Vector2D(velocity.x * 3.0f, velocity.y * 3.0f), 2.5f);
+ giveRepulsion(false, Vector2D(velocity.x * 3.0f, velocity.y * 3.0f), 2.5f);
}
else
{
timer -= delay;
attackTimer -= delay;
if (timer < 0.0f)
{
SoundManager::getInstance().playSound(SOUND_ZOMBIE_00 + rand() % 2);
timer = 5 + rand() % 6;
if (rand() % 3 == 0) clockTurn = !clockTurn;
compute(true);
}
}
checkNextFacing(delay);
frame = ((int)(age * 4.0f)) % 4;
if (frame == 3) frame = 1;
if (facingDirection == 4 || facingDirection == 6) frame += 3;
isMirroring = (facingDirection == 4 );
if (facingDirection == 8) frame += 6;
}
EnemyEntity::animate(delay);
z = y + 17;
}
bool ZombieEntity::attack()
{
Vector2D playerPos = game().getPlayerPosition();
bool attacking = false;
// left ?
if (playerPos.x < x && playerPos.y > y - 15 && playerPos.y < y + 15 && canSee(playerPos.x, playerPos.y))
{
direction = 3;
velocity.x = -creatureSpeed;
velocity.y = 0;
facingDirection = 4;
attacking = true;
}
// right ?
else if (playerPos.x > x && playerPos.y > y - 15 && playerPos.y < y + 15 && canSee(playerPos.x, playerPos.y))
{
direction = 1;
velocity.x = creatureSpeed;
velocity.y = 0;
facingDirection = 6;
attacking = true;
}
// down ?
else if (playerPos.y > y && playerPos.x > x - 15 && playerPos.x < x + 15 && canSee(playerPos.x, playerPos.y))
{
direction = 2;
velocity.x = 0;
velocity.y = creatureSpeed;
facingDirection = 2;
attacking = true;
}
// up ?
else if (playerPos.y < y && playerPos.x > x - 15 && playerPos.x < x + 15 && canSee(playerPos.x, playerPos.y))
{
direction = 0;
velocity.x = 0;
velocity.y = -creatureSpeed;
facingDirection = 8;
attacking = true;
}
if (attacking)
{
SoundManager::getInstance().playSound(SOUND_ZOMBIE_ATTACKING);
facingTimer = 0.2f;
nextFacingDirection = facingDirection;
}
return attacking;
}
void ZombieEntity::compute(bool turn)
{
if (turn)
{
if (clockTurn)
{
direction++;
if (direction == 4) direction = 0;
}
else
{
direction--;
if (direction < 0) direction = 3;
}
}
switch (direction)
{
case 0:
velocity.x = 0;
velocity.y = -creatureSpeed;
nextFacingDirection = 8;
break;
case 1:
velocity.x = creatureSpeed;
velocity.y = 0;
nextFacingDirection = 6;
break;
case 2:
velocity.x = 0;
velocity.y = creatureSpeed;
nextFacingDirection = 2;
break;
case 3:
velocity.x = -creatureSpeed;
velocity.y = 0;
nextFacingDirection = 4;
break;
}
}
void ZombieEntity::calculateBB()
{
boundingBox.left = (int)x - 14;
boundingBox.width = 28;
boundingBox.top = (int)y - 18;
boundingBox.height = 36;
}
void ZombieEntity::collideMapRight()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
else compute(true);
}
void ZombieEntity::collideMapLeft()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.x = -recoil.velocity.x * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.x = -repulsion.velocity.x * 0.3f;
+ else (repulsion.active = false);
}
else compute(true);
}
void ZombieEntity::collideMapTop()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
else compute(true);
}
void ZombieEntity::collideMapBottom()
{
- if (recoil.active)
+ if (repulsion.active)
{
- if (recoil.stun) recoil.velocity.y = -recoil.velocity.y * 0.3f;
- else (recoil.active = false);
+ if (repulsion.stun) repulsion.velocity.y = -repulsion.velocity.y * 0.3f;
+ else (repulsion.active = false);
}
else compute(true);
}
void ZombieEntity::collideWithEnemy(EnemyEntity* entity)
{
if (entity->getMovingStyle() == movWalking)
{
Vector2D recoilVector = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 50.0f);
- giveRecoil(false, recoilVector, 0.2f);
+ giveRepulsion(false, recoilVector, 0.2f);
compute(true);
}
}
void ZombieEntity::drop()
{
if (!invocated) EnemyEntity::drop();
}
bool ZombieEntity::isAttacking()
{
return attackTimer > 1.0f;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Feb 7, 2:07 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55765
Default Alt Text
(945 KB)

Event Timeline